As per ticket and as per Caley's request, so everything can be
edited together.
Also pluralise labels for billing contact info, to indicate
that putting multiple in is ok, and widen the input fields so
that it is more comfortable to input multiple contact details.
Form can be pre-filled with existing data upon instantiation.
WTForms will know not to do this on POST request.
Co-authored-by: Chris Hill-Scott <me@quis.cc>
We have lots of functions for converting various types of data into
strings to be displayed to the user somewhere.
This commit collects all these functions into their own module, rather
than having them cluttering up `app/__init__.py` or buried amongst
various other things that have ended up in `app/utils.py`.
It’s one of the things we check when someone makes a request to go live,
and putting it in the ticket means we don’t have to take the extra step
of clicking into the settings.
Also added some line breaks to chunk things up a bit more clearly.
Includes changing form.enabled to use
OnOffField, for consistency with other on/off
fields.
OnOffField's data is a boolean, not a string, so
some of the logic using it needed to be changed.
Live services shouldn't be able to request to go live again. Once a
service is live we remove the option to go live from the Settings page,
but we still link to the page to request to go live from other places
e.g. the 'Get started' page. As a result, we've seen some services make
another request to go live when their service has already been live for
months - this change will stop that from happening.
We’ve decided that this feature should be self-service, and on by
default for all users. Before we can make it on by default we should
give users a way to switch it off for themselves. Which is what this PR
does.
This was broken because current_service doesn’t update itself after
calling the `update` method of the API. So we thought we were changing
the permissions like this:
```
{'email', 'sms', 'letter'}
{'email', 'sms', 'letter', 'broadcast'}
{'sms', 'letter', 'broadcast'}
{'letter', 'broadcast'}
{'broadcast'}
```
But actually we were doing this:
```
{'email', 'sms', 'letter'}
{'email', 'sms', 'letter', 'broadcast'}
{'sms', 'letter'}
{'email', 'letter'}
{'email', 'sms'}
```
This commit changes the code to update the permissions like this:
```
{'email', 'sms', 'letter'}
{'broadcast'}
```
It does so by adding a new method to the service model which changes all
the permissions in one API call, and updates the tests to mock the
underlying API call, not the method on the model.
When a service is switched over to broadcast it has the email, text
message and letter permissions removed. And the links to switch these
settings back on are hidden.
This commit ensures that even if the user manually goes to the URLs for
these pages, they still won’t be able to switch the other channels back
on.
To make the interface as simple as possible we don’t want to mix up
sending other types of communication where services have the broadcast
permission.
This commit removes the other permissions once a service has been given
the broadcast permission by a platform admin user.
We’re removing it for performance reasons.
This means removing the old pages that edited the letter contact block
when it was stored directly on the service, rather than the current
model where a service can have multiple contact blocks.
'Session expired' or similar makes it sound like a new error.
It could confuse the user and make them think the sign in didn't work
and that their session has expired again.
So we went with:
The change you made was not saved. Please try again.
When the admin app gets user objects from the API, these include a dict
of permissions by service for what the user can do to that services.
Permissions for inactive services are not included in the response as
per:
87cb6f2597/app/dao/permissions_dao.py (L66)
However, this causes a bug where a service is archived but cached user
data still tells us that the user has permissions to view the service.
This should not be the case and causes errors where users can still see
the archived service page, it's settings, and even request to go live
for it, because they are using old cached data for the user.
We solve this by deleting the users who are part of the service from the
cache.
We also delete the templates for this service from the cache as the
templates are also archived when we ask the API to archive the service
as per:
d95c0131e0/app/service/rest.py (L597)
Note, one decision I had to make was whether to delete the user cache
for just active team members or also invited users. Assuming an invited
user can't see the service until they've accepted their invite anyway, it
shouldn't make any difference whether we delete their cache or not.
A lot of pages in the admin app are now generated entirely from Redis,
without touching the API.
The one remaining API call that a lot of pages make, when the user is
platform admin or a member of an organisation, is to get the name of
the current service’s organisation.
This commit adds some code to start caching that as well, which should
speed up page load times for when we’re clicking around the admin app
(it’s typically 100ms just to get the organisation, and more than that
when the API is under load).
This means changing the service model to get the organisation from the
API by ID, not by service ID. Otherwise it would be very hard to clear
the cache if the name of the organisation ever changed.
We can’t cache the whole organisation because it has a
`count_of_live_services` field which can change at any time, without an
update being made.
Rather than hard-coding a format string in a bunch of different places
we can use the function we already have in utils.
This commit also refactors some logic around password resets to put the
date-parsing changes in the most sensible bit of the codebase, so it’s
clearer what the intention of the view-layer code is.
Celery/SQS underperforms in low-traffic environments. Tasks will sit on
celery queues for several seconds before getting picked up if they're
the only thing on the queue. This is observable in our test environments
like preview and staging, but we've got enough load on production that
this isn't an issue.
When we validate reply to email addresses, we expect a delivery receipt
to have been processed within 45 seconds of the button being pressed. On
preview, we often observe times over that, possibly due to the several
queues involved in sending an email and processing its receipt. So, to
ensure that functional tests can pass (when we don't really care how
fast things are, just that the flow doesn't break), bump this timeout up
to 120 seconds on preview. The functional tests were waiting for 120
seconds for the reply to address to be validated anyway.
Requests to go live and email branding requests come through to Zendesk
with tags attached automatically.
With the revised taxonomy some of these tags need to be updated, as
summarised in this spreadsheet.
In addition, `notify_action` tag has to be added in each of those cases.
Old|New
---|---
`notify_request_to_go_live_complete`|`notify_go_live_complete`
`notify_request_to_go_live_incomplete`|`notify_go_live_incomplete`
`notify_action_add_branding`|`notify_branding`
`notify_request_to_go_live_incomplete_mou`|`notify_go_live_incomplete_mou`
`notify_request_to_go_live`|`notify_go_live`
– https://docs.google.com/spreadsheets/d/1o5ATsFsVK8Qpj7x8QvxX-SfEuBZ75028GEySVcdBFYU/edit#gid=0
– https://www.pivotaltracker.com/story/show/169842970
We mostly rely on the API returning a 404 to generate 404s for trying
to get things with non-UUID IDs. This is fine, except our tests often
mock these API calls. So it could look like everything is working fine,
except the thing your passing in might never be a valid UUID, and thus
would 404 in a non-test environment.
So this commit:
1. uses the `uuid` URL converter everywhere there’s something that looks
like an ID in a URL parameter
2. adds a test which automates checking for 1.
Letting people input a bit of free text should reduce the amount of back
and forth we have to do over support tickets when setting up someone’s
branding.
If something else is the only option then we don’t show the radio button
at all and have just the free text input on the page (not behind a
progressive disclosure).