There are three related changes:
* Removed legacy timezones from hc.lib.tz.all_timezones
* Added data migration to update existing Check.tz values
* For backwards compatibility, added code to automatically
replace a legacy timezone with a canonical timezone when a
legacy timezone is passed to an API call
I used the timezone mapping on
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
* Instead of check.n_pings (int) use last_ping().n
* Instead of check.last_ping (datetime) use last_ping().created
There is a time gap from creating a flip object to processing
it (sending out an alert). We want the notification to reflect
the check's state at the moment the flip was created. To do this,
we use the Transport.last_ping() helper method which retrieves
the last ping *that is not newer than the flip*.
This commit updates transport classes and templates to use
Transport.last_ping() consistently everywhere.
... and pass it to Transport.notify_flip().
This allows us to pass flip-specific information (the flip timestamp,
the new status) to transport classes.
If the recipient has set up an username, the responses
from Signal will return "number": null.
Example scenario:
* Alice sets up a Signal integration
* Alice sets up an username for their Signal account
* Alice deletes their Signal account
* Healthchecks sends a message to Alice's Signal account
* Signal responds with UNREGISTERED_FAILURE, but the
number field in the response is null
The fix is to:
* Allow null in the "number" field as a valid value
* Ignore the value of the "number" field when reading Signal responses
In some rare cases the last ping can be None (i.e., there was no
last ping). In these cases we would need to omit the
"Last ping was <time interval> ago." part from the message.
To avoid creating a *third* WhatsApp content template, remove
the "Last ping was <time interval> ago." part from the message
altogether.
What are the cases when last ping could be None? Here's one:
* User creates a check
* User sends a "/start" signal
* The grace time passes, the check goes down. There's no
previous "success" or "fail" signal, so the check's
"last_ping" field will be None.
When preparing WhatsApp payload we call naturaltime() and
serialize its output as JSON. naturaltime can return a lazy object
which causes an exception during JSON serialization. The fix
is to wrap naturaltime in str() to force its output to be a string.