emails: add Message-Id, In-Reply-To and References headers (#89284) #1391
Loading…
Reference in New Issue
No description provided.
Delete Branch "wip/89284-email-threading"
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?
253aeea6a8
to27085fb8a8
27085fb8a8
to25ab1d7689
25ab1d7689
to494bc3bcb7
494bc3bcb7
to493463f25c
493463f25c
to9ede153f5f
9ede153f5f
to7da74513ea
7da74513ea
to28019d0056
28019d0056
to7954dd0268
7954dd0268
to73e1168ecd
@ -232,0 +258,4 @@
}
return message_id
def get_references(self, formdata, addresses):
Tu peux utiliser formdata.iter_evolution_parts(klass=EmailEvolutionPart) pour factoriser un peu.
Merci ! C'est fait :)
73e1168ecd
to00509cd119
00509cd119
to345cbb9367
345cbb9367
to091ed148a9
091ed148a9
toe9e209e748
WIP: emails: add Message-Id, In-Reply-To and References headers (#89284)to emails: add Message-Id, In-Reply-To and References headers (#89284)(première lecture rapide)
@ -52,2 +54,4 @@
self.mail_subject = mail_subject
self.mail_body = mail_body
self.message_id = message_id
self.references = references
Je serais pour faire sans "references"; on ne va pas chercher à inventer une hiérarchie aux messages, faire qu'ils soient tous "enfants" du premier ça suffit.
Ok :) Je fais ça !
@ -232,0 +237,4 @@
for tenant in get_publisher().get_tenants():
if tenant.directory == get_publisher().app_dir:
hostname = tenant.hostname
break
Il faut juste utiliser le tenant dans lequel on est, rien parcourir, get_publisher().tenant.hostname.
C'est ce qu'il a fait en première intention mais ça ne marche pas dans les tests de tests/workflow/test_email.py le publisher de la fixture n'est pas initialisé correctement il fait juste pub._set_request(req), et pub.tenant n'est pas créé; il doit manquer peut-être un .set_app_dir(request) ou quelque chose comme cela.
Je confirme, l'ajout du
pub.set_app_dir(req)
dans la fixture résout le problème, mais c'est visiblement manquant pour d'autres tests.La solution a privilégier serait de patcher les fixtures des tests en question (où le tenant semble mal/pas configuré) ?
Oui. (de manière générale le code de l'application ne doit pas contourner des déficiences dans l'infra de tests).
Ok, le commit est en cours :)
@ -232,0 +246,4 @@
if isinstance(formdata.formdef, CardDef):
form_type = 'card'
else:
form_type = 'form'
Tu peux juste faire formdata.formdef.data_sql_prefix (ça donnera carddata ou formdata).
Cool ! Merci :)
@ -232,0 +260,4 @@
def get_references(self, formdata, addresses):
for part in reversed(list(formdata.iter_evolution_parts(klass=EmailEvolutionPart))):
if part.addresses == addresses:
Et partant de l'idée de ne pas créer d'hiéarchie, ici il faut modifier et se contenter du premier message (correspondant aux destinataires) trouvé.
@ -403,0 +454,4 @@
'email_from': email_from,
'attachments': attachments,
'extra_headers': extra_headers,
}
Plutôt juste appeler ça kwargs, get_email_kwargs c'est trop un nom de fonction.
En effet ! C'est corrigé :)
emails: add Message-Id, In-Reply-To and References headers (#89284)to WIP: emails: add Message-Id, In-Reply-To and References headers (#89284)e9e209e748
toce334f68ec
ee7c16d99e
to5940e77177
5940e77177
tofe981923a3
fe981923a3
to55cfb3b791
55cfb3b791
to37c2499446
WIP: emails: add Message-Id, In-Reply-To and References headers (#89284)to emails: add Message-Id, In-Reply-To and References headers (#89284)@ -4135,0 +4146,4 @@
ts = datetime.datetime.now().strftime('%Y%m%d.%H%M%S.')
hostname = 'example.net'
expt_id = f'wcs-carddata-{carddef.id}-{carddata.id}.{ts}[^@]+@{hostname}'
assert re.match(expt_id, headers['Message-Id'])
Ça arrivera quand même parfois que ça foire parce qu'on n'est pas sur la même seconde; je ne vérifierais pas la partie timestamp.
En effet, merci !
@ -47,2 +50,4 @@
def _get_message_id_re(msg_id_fmt):
return re.compile(msg_id_fmt % {'ts': datetime.datetime.now().strftime(r'%Y%m%d\.%H%M%S\.')})
pareil.
@ -77,2 +84,4 @@
get_response().process_after_jobs()
assert emails.count() == 0
for part in formdata.evolution[-1].parts:
assert not isinstance(part, EmailEvolutionPart)
(dans la suite aussi)
Bonne idée, merci !
@ -125,6 +167,12 @@ def test_email(pub, emails):
assert emails.count() == 1
assert emails.get('foobar')['to'] == 'Undisclosed recipients:;'
assert set(emails.get('foobar')['email_rcpt']) == {'foo@localhost', 'bar@localhost', 'baz@localhost'}
# recipients changed, the email is not a reply-to the previous one
Je ne suis pas sûr que ça soit adéquat comme comportement général.
Deux situations :
Il y aurait sans doute à faire une passe par destinataire, récupérer ainsi une liste de "first message id" et les adresses associées, et faire autant d'envois que nécessaire.
Oui ! J'implémente ça :)
37c2499446
to3bbf644749
3bbf644749
to67a3986d3c
emails: add Message-Id, In-Reply-To and References headers (#89284)to WIP: emails: add Message-Id, In-Reply-To and References headers (#89284)67a3986d3c
toa06864ce21
a06864ce21
toa07d855802
a07d855802
tof75f5a0904
WIP: emails: add Message-Id, In-Reply-To and References headers (#89284)to emails: add Message-Id, In-Reply-To and References headers (#89284)f75f5a0904
toac57ae4d8d
ac57ae4d8d
todb6c6f64ae
@ -232,0 +236,4 @@
message_id = 'wcs-%(type)s-%(formdef_id)s-%(formdata_id)s.%(ts)s@%(hostname)s'
message_id %= {
Détail de style mais plutôt mettre le % sur la ligne du haut, on n'utilise pour ainsi dire jamais %=.
Ok !
@ -232,0 +248,4 @@
def get_first_messages_id(self, formdata, addresses):
remaining = set(addresses)
result = {}
for part in list(formdata.iter_evolution_parts(klass=EmailEvolutionPart)):
Il n'est normalement pas nécessaire de passer list() ici.
En effet, merci !
@ -394,2 +428,4 @@
for first_id, id_addr in self.get_first_messages_id(formdata, addresses).items():
message_id = self.get_message_id(formdata)
formdata.evolution[-1].add_part(
EmailEvolutionPart(
Je serais pour avoir un seul EmailEvolutionPart par contenu, ça sera important quand on voudra afficher ceux-ci dans l'interface. (l'agent voudra voir que le message a bien été envoyé à n personnes, ne sera pas intéressé par le fait que le message a en fait été découpé en trois envois parce que tout le monde n'a pas pris le fil dès le début).
Mais il faut peut-être quand même avoir plusieurs message-id, pour que si jamais on doit logguer en aval (genre exim), la distinction y existe. Donc EmailEvolutionPart, j'ai l'impression quil faut a voir un dictionnaire message_ids, pour y avoir la correspondance message-id ←→ destinataire.
A priori de mesures rapides il n'y a pas de danger que deux appels successifs à get_message_id() donnent le même résultat, il faut plus d'une microseconde pour en générer un.
En effet, bonne idée !
@ -427,0 +451,4 @@
email_kwargs = {
**common_kwargs,
**dest_kwargs,
'extra_headers': {'Message-Id': message_id, **reply_headers},
(mets plutôt Message-ID, avec ID en majuscule, c'est davantage commun).
Bien vu ! Merci :)
db6c6f64ae
to836b7a96b5
Je valide mais j'ai posé une petite demande de commentaire.
@ -232,0 +255,4 @@
if len(remaining) == 0:
break
else:
result[None] = (self.get_message_id(formdata), list(remaining))
Je verrais bien une ligne de commentaire pour expliciter le cas où on se trouve à utiliser None.
Oui, merci ! C'est ajouté.
836b7a96b5
to649ae79ca1
649ae79ca1
toaf25e206da