Several telephone numbers per account: one primary, unique across the instance #90
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Depends on
Reference
Postulo/postulo#90
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists
Profile.phoneis a singleCharField, stored in international form (+33612345678) bythe
PhoneFieldincore/phone_field.py. One number, no uniqueness, no primary.Email addresses already work the way this asks: allauth's
EmailAddressgives an accountseveral, exactly one
primary, eachverifiedindependently, and unique across theinstance. So there is a shape to copy, and its edges are already known.
What this asks for
A
PhoneNumbermodel owned by a person, with:than in a form — a database constraint plus the service that sets it;
accounts cannot both claim
+351912345678.The three things to decide before writing any of it
1. Instance-wide uniqueness is a disclosure. It is worth being explicit, because the
project's first commitment is that one person's data never reaches another's. If a number
must be unique across the instance, then "this number is already in use" tells whoever
typed it that somebody else on this server has it -- which is a real fact about another
account, leaked to anybody who can add a number. Email has the same property and allauth
answers it in a specific way; whatever is done here should be a deliberate choice with the
same care, not an inherited default. Options: refuse with a vague message, refuse plainly,
or take it silently and let an administrator resolve the collision. This needs deciding
first because it shapes the constraint, the form and the tests.
2. Uniqueness of what, exactly.
+351912345678and00351912345678and+351 912 345 678are one number written three ways, and only the stored internationalform is comparable. Postulo deliberately keeps an unparseable number exactly as typed
(
phones.pysays so, and means it: refusing to save it would be the worst outcome). So anumber that could not be parsed has no normalised form, and cannot participate in a
uniqueness constraint at all. The rule probably has to be: unique among numbers that
parsed, ignored for those that did not.
3. Verification, or the absence of it. An email address is unique and verified, and
the verification is what makes uniqueness meaningful -- otherwise anybody can squat any
address. Postulo cannot send an SMS and should not start; that is an outbound channel, a
cost and a dependency the project does not have. So these numbers will be unique but
unverified, which means the first account to type a number owns it. Worth stating in the
interface rather than discovering.
Scope
PhoneNumbermodel, owner-scoped like everything else, with the primary constraint.Profile.phoneinto the first row and makes itprimary, losing nothing.
already have one.
to know there are several.
tests/security/gets the ownership sweep, since this is a new table holding a personaldetail.
Classification
Enhancement. Not breaking for anybody's data -- the migration carries the existing number
across -- but it changes a documented model field, so the wiki and the API schema move
with it.
Landed on
0.3.0as1a0f690.The design changed from what this issue proposed, on the maintainer's direction, in three
ways worth recording here.
It is a plugin, not a schema change. Several telephone numbers is an internal plugin
of a new kind,
feature— the first kind that governs a capability of Postulo itselfrather than a conversation with something outside it. Switched off, Postulo shows and uses
the primary number only, which is exactly what the single
phonefield did.Off deletes nothing. The proposal in conversation was that disabling should discard
everything but the primary. It does not: the interface promises, on two pages and in every
language Postulo speaks, that switching a plugin off deletes nothing, and the first kind
whose subject is Postulo's own tables is not where that promise gets an exception. The
rows stay, the page says how many are being kept back, an export carries all of them either
way, and switching it on again finds them unchanged.
Contacts too. Not just an account holder: a contact at a company holds numbers on the
same terms, which is why the rows are a generic relation rather than a foreign key.
The three questions this issue said to decide first were decided as follows.
on the maintainer's decision. The message says plainly that the number is already
recorded on this instance and may belong to somebody else's records, because a vaguer
message discloses exactly as much while leaving the person guessing.
wiki/Tracking-applications.mdsays what else is not disclosed: not whose, not where, not when.
could parse is kept as typed and takes part in no comparison — the rule
phones.pyalready sets.
normalise()also reads00as the international prefix, so one numberwritten three ways is one number.
account to type a number holds it.
One thing this issue did not anticipate: existing data was never held to the rule, and two
contacts sharing a switchboard is not a mistake. The data migration grandfathers such pairs
rather than picking a winner, and the form tells you the first time one is edited.