From c813c0297561123adffb4a193432912672c52df0 Mon Sep 17 00:00:00 2001 From: mfo Date: Fri, 7 Jun 2024 06:23:52 +0200 Subject: [PATCH 1/3] feat(EmailChecker.check): add class to search for typo in email addresses --- Gemfile | 1 + Gemfile.lock | 2 + app/lib/email_checker.rb | 649 +++++++++++++++++++++++++++++++++ spec/lib/email_checker_spec.rb | 36 ++ 4 files changed, 688 insertions(+) create mode 100644 app/lib/email_checker.rb create mode 100644 spec/lib/email_checker_spec.rb diff --git a/Gemfile b/Gemfile index 8d78db84d..9a9ff1600 100644 --- a/Gemfile +++ b/Gemfile @@ -95,6 +95,7 @@ gem 'sidekiq' gem 'sidekiq-cron' gem 'skylight' gem 'spreadsheet_architect' +gem 'string-similarity' gem 'strong_migrations' # lint database migrations gem 'sys-proctable' gem 'turbo-rails' diff --git a/Gemfile.lock b/Gemfile.lock index 51281c712..98bde51e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -765,6 +765,7 @@ GEM activesupport (>= 5.2) sprockets (>= 3.0.0) stackprof (0.2.26) + string-similarity (2.1.0) stringio (3.1.0) strong_migrations (1.8.0) activerecord (>= 5.2) @@ -1013,6 +1014,7 @@ DEPENDENCIES spring spring-commands-rspec stackprof + string-similarity strong_migrations sys-proctable timecop diff --git a/app/lib/email_checker.rb b/app/lib/email_checker.rb new file mode 100644 index 000000000..97fa9d803 --- /dev/null +++ b/app/lib/email_checker.rb @@ -0,0 +1,649 @@ +class EmailChecker + KNOWN_DOMAINS = [ + 'gmail.com', + 'hotmail.fr', + 'orange.fr', + 'yahoo.fr', + 'hotmail.com', + 'outlook.fr', + 'wanadoo.fr', + 'free.fr', + 'yahoo.com', + 'icloud.com', + 'laposte.net', + 'live.fr', + 'sfr.fr', + 'outlook.com', + 'neuf.fr', + 'aol.com', + 'bbox.fr', + 'msn.com', + 'me.com', + 'gmx.fr', + 'protonmail.com', + 'club-internet.fr', + 'live.com', + 'ymail.com', + 'ars.sante.fr', + 'mail.ru', + 'cegetel.net', + 'numericable.fr', + 'aliceadsl.fr', + 'comcast.net', + 'assurance-maladie.fr', + 'mac.com', + 'naver.com', + 'airbus.com', + 'justice.fr', + 'pole-emploi.fr', + 'educagri.fr', + 'aphp.fr', + 'netcourrier.com', + 'dbmail.com', + 'aol.fr', + 'qq.com', + 'hotmail.co.uk', + 'yahoo.co.uk', + 'proxima-mail.fr', + 'yahoo.com.br', + 'sciencespo.fr', + 'gmx.com', + 'etu.univ-st-etienne.fr', + 'yahoo.ca', + '163.com', + 'francetravail.fr', + 'mail.pf', + 'nantesmetropole.fr', + 'hotmail.it', + 'sbcglobal.net', + 'noos.fr', + 'ird.fr', + 'safrangroup.com', + 'croix-rouge.fr', + 'eiffage.com', + 'veolia.com', + 'notaires.fr', + 'nordnet.fr', + 'videotron.ca', + 'paris.fr', + 'lilo.org', + 'mfr.asso.fr', + 'yopmail.com', + 'ukr.net', + 'onf.fr', + 'stellantis.com', + '9online.fr', + 'atmp50.fr', + 'engie.com', + 'libertysurf.fr', + 'mailo.com', + 'auchan.fr', + 'verizon.net', + 'rocketmail.com', + 'mpsa.com', + 'entrepreneur.fr', + 'googlemail.com', + 'arcelormittal.com', + 'groupe-sos.org', + 'proton.me', + 'att.net', + 'pm.me', + 'orange.com', + 'abv.bg', + 'yahoo.es', + 'creditmutuel.fr', + 'yandex.ru', + 'essec.edu', + 'urssaf.fr', + 'bpifrance.fr', + 'uol.com.br', + 'suez.com', + 'univ-st-etienne.fr', + 'korian.fr', + 'developpement-durable.gouv.fr', + 'modernisation.gouv.fr', + 'social.gouv.fr', + 'emploi.gouv.fr', + 'agriculture.gouv.fr', + 'intradef.gouv.fr', + 'interieur.gouv.fr', + 'oise.gouv.fr', + 'direccte.gouv.fr', + 'culture.gouv.fr', + 'pas-de-calais.gouv.fr', + 'finances.gouv.fr', + 'drieets.gouv.fr', + 'drjscs.gouv.fr', + 'sg.social.gouv.fr', + 'martinique.pref.gouv.fr', + 'beta.gouv.fr', + 'dieccte.gouv.fr', + 'cotes-darmor.gouv.fr', + 'vosges.gouv.fr', + 'developppement-durable.gouv.fr', + 'mayenne.gouv.fr', + 'aviation-civile.gouv.fr', + 'data.gouv.fr', + 'recherche.gouv.fr', + 'sante.gouv.fr', + 'paris-idf.gouv.fr', + 'guyane.gouv.fr', + 'douane.finances.gouv.fr', + 'cget.gouv.fr', + 'herault.gouv.fr', + 'loire-atlantique.gouv.fr', + 'manche.gouv.fr', + 'seine-maritime.gouv.fr', + 'dgccrf.finances.gouv.fr', + 'tarn-et-garonne.gouv.fr', + 'dila.gouv.fr', + 'diplomatie.gouv.fr', + 'haut-rhin.gouv.fr', + 'nord.gouv.fr', + 'bouches-du-rhone.gouv.fr', + 'alpes-de-haute-provence.gouv.fr', + 'hautes-alpes.gouv.fr', + 'alpes-maritimes.gouv.fr', + 'var.gouv.fr', + 'vaucluse.gouv.fr', + 'rhone.gouv.fr', + 'occitanie.gouv.fr', + 'ille-et-vilaine.gouv.fr', + 'finistere.gouv.fr', + 'aisne.gouv.fr', + 'indre.gouv.fr', + 'yvelines.gouv.fr', + 'bas-rhin.gouv.fr', + 'landes.gouv.fr', + 'haute-marne.gouv.fr', + 'correze.gouv.fr', + 'val-doise.gouv.fr', + 'seine-et-marne.gouv.fr', + 'essonne.gouv.fr', + 'calvados.gouv.fr', + 'charente-maritime.gouv.fr', + 'corse-du-sud.gouv.fr', + 'gironde.gouv.fr', + 'haute-corse.gouv.fr', + 'morbihan.gouv.fr', + 'pyrenees-atlantiques.gouv.fr', + 'pyrenees-orientales.gouv.fr', + 'somme.gouv.fr', + 'vendee.gouv.fr', + 'dgtresor.gouv.fr', + 'marne.gouv.fr', + 'auvergne-rhone-alpes.gouv.fr', + 'meurthe-et-moselle.gouv.fr', + 'pm.gouv.fr', + 'oncfs.gouv.fr', + 'orne.gouv.fr', + 'charente.gouv.fr', + 'travail.gouv.fr', + 'gard.gouv.fr', + 'maine-et-loire.gouv.fr', + 'moselle.gouv.fr', + 'outre-mer.gouv.fr', + 'jscs.gouv.fr', + 'haute-garonne.gouv.fr', + 'vienne.gouv.fr', + 'dordogne.gouv.fr', + 'eure.gouv.fr', + 'meuse.gouv.fr', + 'savoie.gouv.fr', + 'doubs.gouv.fr', + 'bfc.gouv.fr', + 'education.gouv.fr', + 'ariege.gouv.fr', + 'normandie.gouv.fr', + 'gendarmerie.interieur.gouv.fr', + 'ain.gouv.fr', + 'ardennes.gouv.fr', + 'drome.gouv.fr', + 'bretagne.gouv.fr', + 'paca.gouv.fr', + 'haute-saone.gouv.fr', + 'lot.gouv.fr', + 'dgfip.finances.gouv.fr', + 'aveyron.gouv.fr', + 'gers.gouv.fr', + 'tarn.gouv.fr', + 'aude.gouv.fr', + 'lozere.gouv.fr', + 'hautes-pyrenees.gouv.fr', + 'jeunesse-sports.gouv.fr', + 'alpes.maritimes.gouv.fr', + 'dreets.gouv.fr', + 'justice.gouv.fr', + 'sports.gouv.fr', + 'nouvelle-aquitaine.gouv.fr', + 'jura.gouv.fr', + 'haute-savoie.gouv.fr', + 'creuse.gouv.fr', + 'creps-poitiers.sports.gouv.fr', + 'equipement-agriculture.gouv.fr', + 'ira-metz.gouv.fr', + 'loire.gouv.fr', + 'defense.gouv.fr', + 'paris.gouv.fr', + 'ensm.sports.gouv.fr', + 'isere.gouv.fr', + 'haute-loire.gouv.fr', + 'cantal.gouv.fr', + 'lot-et-garonne.gouv.fr', + 'reunion.pref.gouv.fr', + 'loiret.gouv.fr', + 'indre-et-loire.gouv.fr', + 'eleve.ira-metz.gouv.fr', + 'deux-sevres.gouv.fr', + 'inao.gouv.fr', + 'franceconnect.gouv.fr', + 'essone.gouv.fr', + 'workinfrance.beta.gouv.fr', + 'seine-saint-denis.gouv.fr', + 'val-de-marne.gouv.fr', + 'morbihan.pref.gouv.fr', + 'externes.justice.gouv.fr', + 'haute-vienne.gouv.fr', + 'territoire-de-belfort.gouv.fr', + 'creps-reunion.sports.gouv.fr', + 'creps-centre.sports.gouv.fr', + 'creps-rhonealpes.sports.gouv.fr', + 'creps-montpellier.sports.gouv.fr', + 'nord.pref.gouv.fr', + 'charente-maritime.pref.gouv.fr', + 'cher.gouv.fr', + 'cote-dor.gouv.fr', + 'ssi.gouv.fr', + 'ira.gouv.fr', + 'pays-de-la-loire.gouv.fr', + 'loir-et-cher.gouv.fr', + 'saone-et-loire.gouv.fr', + 'enseignementsup.gouv.fr', + 'eure-et-loir.gouv.fr', + 'yonne.gouv.fr', + 'guadeloupe.pref.gouv.fr', + 'centre-val-de-loire.gouv.fr', + 'entreprise.api.gouv.fr', + 'grand-est.gouv.fr', + 'sarthe.gouv.fr', + 'sarthe.pref.gouv.fr', + 'puy-de-dome.gouv.fr', + 'externes.sante.gouv.fr', + 'allier.gouv.fr', + 'aube.gouv.fr', + 'nievre.gouv.fr', + 'ardeche.gouv.fr', + 'api.gouv.fr', + 'hauts-de-seine.gouv.fr', + 'hauts-de-france.gouv.fr', + 'temp-beta.gouv.fr', + 'def.gouv.fr', + 'particulier.api.gouv.fr', + 'ira-lille.gouv.fr', + 'haute-saone.pref.gouv.fr', + 'yvelines.pref.gouv.fr', + 'sgg.pm.gouv.fr', + 'anah.gouv.fr', + 'corse.gouv.fr', + 'mayenne.pref.gouv.fr', + 'cote-dor.pref.gouv.fr', + 'guyane.pref.gouv.fr', + 'ira-nantes.gouv.fr', + 'igas.gouv.fr', + 'tarn.pref.gouv.fr', + 'martinique.gouv.fr', + 'creps-paca.sports.gouv.fr', + 'ofb.gouv.fr', + 'loir-et-cher.pref.gouv.fr', + 'indre-et-loire.pref.gouv.fr', + 'polynesie-francaise.pref.gouv.fr', + 'scl.finances.gouv.fr', + 'numerique.gouv.fr', + 'cantal.pref.gouv.fr', + 'territoire-de-belfort.pref.gouv.fr', + 'creps-wattignies.sports.gouv.fr', + 'vienne.pref.gouv.fr', + 'ardennes.pref.gouv.fr', + 'creps-strasbourg.sports.gouv.fr', + 'creps-dijon.sports.gouv.fr', + 'ara.gouv.fr', + 'sgdsn.gouv.fr', + 'pays-de-la-loire.pref.gouv.fr', + 'anct.gouv.fr', + 'creps-pap.sports.gouv.fr', + 'sgae.gouv.fr', + 'esnm.sports.gouv.fr', + 'nouvelle-caledonie.gouv.fr', + 'deets.gouv.fr', + 'mayotte.gouv.fr', + 'creps-bordeaux.sports.gouv.fr', + 'civs.gouv.fr', + 'iga.interieur.gouv.fr', + 'cab.travail.gouv.fr', + 'ira-bastia.gouv.fr', + 'ira-lyon.gouv.fr', + 'creps-lorraine.sports.gouv.fr', + 'dihal.gouv.fr', + 'ofpra.gouv.fr', + 'mayotte.pref.gouv.fr', + 'strategie.gouv.fr', + 'territoires.gouv.fr', + 'dgcl.gouv.fr', + 'doubs.pref.gouv.fr', + 'service-civique.gouv.fr', + 'maine-et-loire.pref.gouv.fr', + 'envsn.sports.gouv.fr', + 'wallis-et-futuna.pref.gouv.fr', + 'gendarmerie.defense.gouv.fr', + 'anlci.gouv.fr', + 'cabinets.finances.gouv.fr', + 'seine-maritime.pref.gouv.fr', + 'promo46.ira-metz.gouv.fr', + 'aisne.pref.gouv.fr', + 'sportsdenature.gouv.fr', + 'loire-atlantique.pref.gouv.fr', + 'aude.pref.gouv.fr', + 'premier-ministre.gouv.fr', + 'igf.finances.gouv.fr', + 'eleves.ira-bastia.gouv.fr', + 'igesr.gouv.fr', + 'alpc.gouv.fr', + 'externes.emploi.gouv.fr', + 'prestataire.finances.gouv.fr', + 'gironde.pref.gouv.fr', + 'premar-atlantique.gouv.fr', + 'creps-toulouse.sports.gouv.fr', + 'guadeloupe.gouv.fr', + 'cybermalveillance.gouv.fr', + 'dicod.defense.gouv.fr', + 'creps-vichy.sports.gouv.fr', + 'aft.gouv.fr', + 'equipement.gouv.fr', + 'academie.defense.gouv.fr', + 'aube.pref.gouv.fr', + 'seine-et-marne.pref.gouv.fr', + 'pyrenees-orientales.pref.gouv.fr', + 'haute-garonne.pref.gouv.fr', + 'haut-rhin.pref.gouv.fr', + 'seine-saint-denis.pref.gouv.fr', + 'dcstep.gouv.fr', + 'promo47.ira-metz.gouv.fr', + 'trackdechets.beta.gouv.fr', + 'val-de-marne.pref.gouv.fr', + 'fabrique.social.gouv.fr', + 'agrasc.gouv.fr', + 'indre.pref.gouv.fr', + 'tarn-et-garonne.pref.gouv.fr', + 'corse.pref.gouv.fr', + 'bas-rhin.pref.gouv.fr', + 'inclusion.beta.gouv.fr', + 'hauts-de-seine.pref.gouv.fr', + 'loiret.pref.gouv.fr', + 'essonne.pref.gouv.fr', + 'territoires-industrie.gouv.fr', + 'spm975.gouv.fr', + 'saint-barth-saint-martin.gouv.fr', + 'judiciaire.interieur.gouv.fr', + 'mer.gouv.fr', + 'premar-manche.gouv.fr', + 'haute-normandie.pref.gouv.fr', + 'prestataire.modernisation.gouv.fr', + 'covoiturage.beta.gouv.fr', + 'promo48.ira-metz.gouv.fr', + 'france-services.gouv.fr', + 'ddets.gouv.fr', + 'afa.gouv.fr', + 'externes.social.gouv.fr', + 'vosges.pref.gouv.fr', + 'reunion.gouv.fr', + 'rhone.pref.gouv.fr', + 'alpes-maritimes.pref.gouv.fr', + 'gard.pref.gouv.fr', + 'oise.pref.gouv.fr', + 'creps-reims.sports.gouv.fr', + 'bouches-du-rhone.pref.gouv.fr', + 'esante.gouv.fr', + 'rhone-alpes.pref.gouv.fr', + 'finistere.pref.gouv.fr', + 'ops-bss.defense.gouv.fr', + 'orne.pref.gouv.fr', + 'transformation.gouv.fr', + 'cbcm.social.gouv.fr', + 'recosante.beta.gouv.fr', + 'pas-de-calais.pref.gouv.fr', + 'promo49.ira-metz.gouv.fr', + 'paca.pref.gouv.fr', + 'meurthe-et-moselle.pref.gouv.fr', + 'externes.sg.social.gouv.fr', + 'puy-de-dome.pref.gouv.fr', + 'academie.def.gouv.fr', + 'tarn.gouv.frd81intranet.ddcspp.tarn.gouv.fr', + 'agriculture-equipement.gouv.fr', + 'creps-idf.sports.gouv.fr', + 'eleve.ira-nantes.gouv.fr', + 'cohesion-territoires.gouv.fr', + 'ariege.pref.gouv.fr', + 'pyrenees-atlantiques.pref.gouv.fr', + 'hautes-pyrenees.pref.gouv.fr', + 'lot-et-garonne.pref.gouv.fr', + 'loire.pref.gouv.fr', + 'info-routiere.gouv.fr', + 'diges.gouv.fr', + 'insp.gouv.fr', + 'creps-pdl.sports.gouv.fr', + 'ddc.social.gouv.fr', + 'eleve.insp.gouv.fr', + 'val-doise.pref.gouv.fr', + 'montsaintmichel.gouv.fr', + 'st-cyr.terre-net.defense.gouv.fr', + '.finances.gouv.fr', + 'logement.gouv.fr', + 'cotes-darmor.pref.gouv.fr', + 'marne.pref.gouv.fr', + 'herault.pref.gouv.fr', + 'viennne.gouv.fr', + 'landes.pref.gouv.fr', + 'moselle.pref.gouv.fr', + 'saone-et-loire.pref.gouv.fr', + 'bmpm.gouv.fr', + 'ecologie-territoires.gouv.fr', + 'nievre.pref.gouv.fr', + 'hautes-pyrénées.gouv.fr', + 'gic.gouv.fr', + 'industrie.gouv.fr', + 'lot.pref.gouv.fr', + 'plan.gouv.fr', + 'internet.gouv.fr', + 'mesads.beta.gouv.fr', + 'gers.pref.gouv.fr', + 'dordogne.pref.gouv.fr', + 'somme.pref.gouv.fr', + 'datasubvention.beta.gouv.fr', + 'anc.gouv.fr', + 'premar-mediterranee.gouv.fr', + 'ille-et-vilaine.pref.gouv.fr', + 'eure-et-loir.pref.gouv.fr', + 'prestataires.pm.gouv.fr', + 'snu.gouv.fr', + 'code.gouv.fr', + 'alsace.pref.gouv.fr', + 'haute-vienne.pref.gouv.fr', + 'yonne.pref.gouv.fr', + 'bretagne.pref.gouv.fr', + 'mastere.insp.gouv.fr', + 'cada.pm.gouv.fr', + 'creuse.pref.gouv.fr', + 'ecologie.gouv.fr', + 'midi-pyrenees.pref.gouv.fr', + 'promo54.ira-metz.gouv.fr', + 'var.pref.gouv.fr', + 'alpes-de-haute-provence.pref.gouv.fr', + 'mail.numerique.gouv.fr', + 'france-identite.gouv.fr', + 'transport.data.gouv.fr', + 'allier.pref.gouv.fr', + 'dilhal.gouv.fr', + 'ardeche.pref.gouv.fr', + 'haute-corse.pref.gouv.fr', + 'intérieur.gouv.fr', + 'ddfip.gouv.fr', + 'calvados.pref.gouv.fr', + 'territoir-de-belfort.gouv.fr', + 'nor.gouv.fr', + 'creps-occitanie.sports.gouv.fr', + 'developpement-durabe.gouv.fr', + 'educ.nat.gouv.fr', + 'developpement-duable.gouv.fr', + 'dgfip.finanes.gouv.fr', + 'loire-atlantqieu.gouv.fr', + 'promo55.ira-metz.gouv.fr', + 'haute-saône.gouv.fr', + 'developpement.durable.gouv.fr', + 'dreet.gouv.fr', + 'miprof.gouv.fr', + 'pref.guyane.gouv.fr', + 'developpement.gouv.fr', + 'gendamrerie.interieur.gouv.fr', + 'pyrenees-atlantique.gouv.fr', + 'apprentissage.beta.gouv.fr', + 'yveliens.gouv.fr', + 'justiice.gouv.fr', + 'cutlure.gouv.fr', + 'aidantsconnect.beta.gouv.fr', + 'developpement-durbale.gouv.fr', + 'sine-et-marne.gouv.fr', + 'sociale.gouv.fr', + 'develeoppement-durable.gouv.fr', + 'draaf.gouv.fr', + 'drets.gouv.fr', + 'ancli.gouv.fr', + 'finistrere.gouv.fr', + 'bourgogne.pref.gouv.fr', + 'ac-polynesie.pf', + 'ac-lille.fr', + 'ac-nantes.fr', + 'ac-martinique.fr', + 'ac-creteil.fr', + 'ac-toulouse.fr', + 'ac-amiensfr', + 'ac-amiens.fr', + 'ac-rennes.fr', + 'ac-strasbourg.fr', + 'ac-lyon.fr', + 'ac-versailles.fr', + 'ac-audit.fr', + 'ac-rouen.fr', + 'ac-reunion.fr', + 'ac-poitiers.fr', + 'ac-caen.fr', + 'ac-montpellier.fr', + 'ac-paris.fr', + 'ac-besancon.fr', + 'ac-nancy-metz.fr', + 'ac-aix-marseille.fr', + 'ac-grenoble.fr', + 'ac-corse.fr', + 'ac-nice.fr', + 'ac-orleans-tours.fr', + 'ac-guadeloupe.fr', + 'ac-reims.fr', + 'ac-mayotte.fr', + 'ac-clermont.fr', + 'ac-bordeaux.fr', + 'ac-limoges.fr', + 'ac-normandie.fr', + 'ac-dijon.fr', + 'ac-guyane.fr', + 'ac-transports.fr', + 'ac-arpajonnais.com', + 'ac-cned.fr', + 'ac-nettoyage.com', + 'ac-architectes.fr', + 'ac-ajaccio.corsica', + 'ac-noumea.nc', + 'ac-spm.fr', + 'ac-versailes.fr', + 'ac-polynesie.fr', + 'ac-experts.fr', + 'ac-creteil.com', + 'ac-smart-relocation.com', + 'ac-ec.pro', + 'ac-sas.fr', + 'ac-derma.de', + 'ac-or.com', + 'ac-baugeois.fr', + 'ac-5.ru', + 'ac-arles.fr', + 'ac-holding.net', + 'ac-mb.fr', + 'ac-wf.wf', + 'ac-brest-finistere.fr', + 'ac-leman.com', + 'ac-darboussier.fr', + 'ac-si.fr', + 'ac-bordeau.fr', + 'ac-gatinais.com', + 'ac-cheminots.fr', + 'ac-seyssinet.com', + 'ac-cannes.fr', + 'ac-prev.com', + 'ac-sologne.fr', + 'ac-rennes', + 'ac-courbevoie.com', + 'ac-ce.fr', + 'ac-architecte.fr', + 'ac-tions.org', + 'ac-pm.fr', + 'ac-avocats.com', + 'ac-talents-rh.com', + 'ac-louis.com', + 'ac-internet.fr', + 'ac-toulouse.com', + 'ac-escial.fr', + 'ac-environnement.com', + 'ac-academie.fr', + 'ac-poiters.fr', + 'ac-bordeux.fr', + 'ac-verseilles.fr', + 'ac-ais-marseille.fr', + 'ac-horizon.fr', + 'ac-bordeaux.ft', + 'ac-toulouses.fr', + 'ac-toulous.fr' + ].freeze + + def self.check(email:) + return { success: false } if email.blank? + + parsed_email = Mail::Address.new(email) + return { success: false } if parsed_email.domain.blank? + + return { success: true } if KNOWN_DOMAINS.any? { _1 == parsed_email.domain } + + similar_domains = closest_domains(domain: parsed_email.domain) + return { success: true } if similar_domains.empty? + + { success: true, email_suggestions: email_suggestions(parsed_email:, similar_domains:) } + end + + private + + def self.closest_domains(domain:) + KNOWN_DOMAINS.filter do |known_domain| + close_by_distance_of(domain, known_domain, distance: 1) || + with_same_chars_and_close_by_distance_of(domain, known_domain, distance: 2) + end + end + + def self.close_by_distance_of(a, b, distance:) + String::Similarity.levenshtein_distance(a, b) == distance + end + + def self.with_same_chars_and_close_by_distance_of(a, b, distance:) + close_by_distance_of(a, b, distance: 2) && a.chars.sort == b.chars.sort + end + + def self.email_suggestions(parsed_email:, similar_domains:) + similar_domains.map { Mail::Address.new("#{parsed_email.local}@#{_1}").to_s } + end +end diff --git a/spec/lib/email_checker_spec.rb b/spec/lib/email_checker_spec.rb new file mode 100644 index 000000000..cfcf73bfa --- /dev/null +++ b/spec/lib/email_checker_spec.rb @@ -0,0 +1,36 @@ +describe EmailChecker do + describe 'check' do + subject { described_class } + + it 'works with identified use cases' do + expect(subject.check(email: nil)).to eq({ success: false }) + expect(subject.check(email: '')).to eq({ success: false }) + expect(subject.check(email: 'panpan')).to eq({ success: false }) + + # allow same domain + expect(subject.check(email: "martin@orange.fr")).to eq({ success: true }) + # find difference of 1 lev distance + expect(subject.check(email: "martin@orane.fr")).to eq({ success: true, email_suggestions: ['martin@orange.fr'] }) + # find difference of 2 lev distance, only with same chars + expect(subject.check(email: "martin@oragne.fr")).to eq({ success: true, email_suggestions: ['martin@orange.fr'] }) + # ignore unknown domain + expect(subject.check(email: "martin@ore.fr")).to eq({ success: true }) + end + + it 'passes through real use cases, with levenshtein_distance 1' do + expect(subject.check(email: "martin@asn.com")).to eq({ success: true, email_suggestions: ['martin@msn.com'] }) + expect(subject.check(email: "martin@gamail.com")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] }) + expect(subject.check(email: "martin@glail.com")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] }) + expect(subject.check(email: "martin@gmail.coml")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] }) + expect(subject.check(email: "martin@gmail.con")).to eq({ success: true, email_suggestions: ['martin@gmail.com'] }) + expect(subject.check(email: "martin@hotmil.fr")).to eq({ success: true, email_suggestions: ['martin@hotmail.fr'] }) + expect(subject.check(email: "martin@mail.com")).to eq({ success: true, email_suggestions: ["martin@gmail.com", "martin@ymail.com", "martin@mailo.com"] }) + expect(subject.check(email: "martin@msc.com")).to eq({ success: true, email_suggestions: ["martin@msn.com", "martin@mac.com"] }) + expect(subject.check(email: "martin@ymail.com")).to eq({ success: true }) + end + + it 'passes through real use cases, with levenshtein_distance 2, must share all chars' do + expect(subject.check(email: "martin@oise.fr")).to eq({ success: true }) # could be live.fr + end + end +end From 66eb3dc821d924f9a3143398ad2263b3b54c2485 Mon Sep 17 00:00:00 2001 From: mfo Date: Fri, 7 Jun 2024 10:06:40 +0200 Subject: [PATCH 2/3] feat(email_check): change strategy to check email, dropping email_buttler package and using a custom EmailChecker --- app/components/dsfr/input_component.rb | 2 +- app/controllers/email_checker_controller.rb | 5 +++ .../controllers/email_input_controller.ts | 31 +++++++++++--- bun.lockb | Bin 502268 -> 501940 bytes config/routes.rb | 1 + package.json | 1 - .../email_checker_controller_spec.rb | 39 ++++++++++++++++++ 7 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 app/controllers/email_checker_controller.rb create mode 100644 spec/controllers/email_checker_controller_spec.rb diff --git a/app/components/dsfr/input_component.rb b/app/components/dsfr/input_component.rb index 3ee07149b..367ed74b0 100644 --- a/app/components/dsfr/input_component.rb +++ b/app/components/dsfr/input_component.rb @@ -32,7 +32,7 @@ class Dsfr::InputComponent < ApplicationComponent }.merge(input_group_error_class_names)) } if email? - opts[:data] = { controller: 'email-input' } + opts[:data] = { controller: 'email-input', email_input_url_value: show_email_suggestions_path } end opts end diff --git a/app/controllers/email_checker_controller.rb b/app/controllers/email_checker_controller.rb new file mode 100644 index 000000000..b794b4d7a --- /dev/null +++ b/app/controllers/email_checker_controller.rb @@ -0,0 +1,5 @@ +class EmailCheckerController < ApplicationController + def show + render json: EmailChecker.check(email: params[:email]) + end +end diff --git a/app/javascript/controllers/email_input_controller.ts b/app/javascript/controllers/email_input_controller.ts index 8eed97fa9..8b64a7e92 100644 --- a/app/javascript/controllers/email_input_controller.ts +++ b/app/javascript/controllers/email_input_controller.ts @@ -1,18 +1,39 @@ -import { suggest } from 'email-butler'; +import { httpRequest } from '@utils'; import { show, hide } from '@utils'; import { ApplicationController } from './application_controller'; +type checkEmailResponse = { + success: boolean; + email_suggestions: string[]; +}; + export class EmailInputController extends ApplicationController { static targets = ['ariaRegion', 'suggestion', 'input']; + static values = { + url: String + }; + + declare readonly urlValue: string; + declare readonly ariaRegionTarget: HTMLElement; declare readonly suggestionTarget: HTMLElement; declare readonly inputTarget: HTMLInputElement; - checkEmail() { - const suggestion = suggest(this.inputTarget.value); - if (suggestion && suggestion.full) { - this.suggestionTarget.innerHTML = suggestion.full; + async checkEmail() { + if (!this.inputTarget.value) { + return; + } + + const url = new URL(this.urlValue, document.baseURI); + url.searchParams.append('email', this.inputTarget.value); + + const data: checkEmailResponse | null = await httpRequest( + url.toString() + ).json(); + + if (data && data.email_suggestions && data.email_suggestions.length > 0) { + this.suggestionTarget.innerHTML = data.email_suggestions[0]; show(this.ariaRegionTarget); this.ariaRegionTarget.setAttribute('aria-live', 'assertive'); } diff --git a/bun.lockb b/bun.lockb index 9cc8b3bb356a97e0f908f76bd947b656fda4a8ab..7c777a1117455aa446b2e741a83ac6e465d43f38 100755 GIT binary patch delta 86005 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 Pg5Km5G8Gr5*!I5wC7i}^ delta 86386 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 diff --git a/config/routes.rb b/config/routes.rb index c4973d1ae..8b5f2152e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -161,6 +161,7 @@ Rails.application.routes.draw do end get 'password_complexity' => 'password_complexity#show', as: 'show_password_complexity' + get 'check_email' => 'email_checker#show', as: 'show_email_suggestions' resources :targeted_user_links, only: [:show] diff --git a/package.json b/package.json index 8d5144609..edad7ebc6 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "core-js": "^3.37.1", "date-fns": "^2.30.0", "debounce": "^1.2.1", - "email-butler": "^1.0.13", "geojson": "^0.5.0", "graphiql": "^3.2.3", "graphql": "^16.8.1", diff --git a/spec/controllers/email_checker_controller_spec.rb b/spec/controllers/email_checker_controller_spec.rb new file mode 100644 index 000000000..4572c2cd4 --- /dev/null +++ b/spec/controllers/email_checker_controller_spec.rb @@ -0,0 +1,39 @@ +describe EmailCheckerController, type: :controller do + describe '#show' do + render_views + before { get :show, format: :json, params: params } + let(:body) { JSON.parse(response.body, symbolize_names: true) } + + context 'valid email' do + let(:params) { { email: 'martin@orange.fr' } } + it do + expect(response).to have_http_status(:success) + expect(body).to eq({ success: true }) + end + end + + context 'email with typo' do + let(:params) { { email: 'martin@orane.fr' } } + it do + expect(response).to have_http_status(:success) + expect(body).to eq({ success: true, email_suggestions: ['martin@orange.fr'] }) + end + end + + context 'empty' do + let(:params) { { email: '' } } + it do + expect(response).to have_http_status(:success) + expect(body).to eq({ success: false }) + end + end + + context 'notanemail' do + let(:params) { { email: 'clarkkent' } } + it do + expect(response).to have_http_status(:success) + expect(body).to eq({ success: false }) + end + end + end +end From 8e3d45b0b1e0872fb2523b9ecad885854fcea0b0 Mon Sep 17 00:00:00 2001 From: mfo Date: Tue, 11 Jun 2024 10:17:27 +0200 Subject: [PATCH 3/3] review(pr): some enhancement, tx @colinux Co-Authored-By: Colin Darie --- app/controllers/email_checker_controller.rb | 2 +- .../controllers/email_input_controller.ts | 6 +++++- app/lib/email_checker.rb | 15 +++++++++------ spec/lib/email_checker_spec.rb | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app/controllers/email_checker_controller.rb b/app/controllers/email_checker_controller.rb index b794b4d7a..19cd0493b 100644 --- a/app/controllers/email_checker_controller.rb +++ b/app/controllers/email_checker_controller.rb @@ -1,5 +1,5 @@ class EmailCheckerController < ApplicationController def show - render json: EmailChecker.check(email: params[:email]) + render json: EmailChecker.new.check(email: params[:email]) end end diff --git a/app/javascript/controllers/email_input_controller.ts b/app/javascript/controllers/email_input_controller.ts index 8b64a7e92..f8442e1d3 100644 --- a/app/javascript/controllers/email_input_controller.ts +++ b/app/javascript/controllers/email_input_controller.ts @@ -21,7 +21,11 @@ export class EmailInputController extends ApplicationController { declare readonly inputTarget: HTMLInputElement; async checkEmail() { - if (!this.inputTarget.value) { + if ( + !this.inputTarget.value || + this.inputTarget.value.length < 5 || + !this.inputTarget.value.includes('@') + ) { return; } diff --git a/app/lib/email_checker.rb b/app/lib/email_checker.rb index 97fa9d803..c2cbe3536 100644 --- a/app/lib/email_checker.rb +++ b/app/lib/email_checker.rb @@ -1,4 +1,7 @@ class EmailChecker + # Extracted 100 most used domain on our users table [june 2024] + # + all .gouv.fr domain on our users table + # + all .ac-xxx on our users table KNOWN_DOMAINS = [ 'gmail.com', 'hotmail.fr', @@ -612,10 +615,10 @@ class EmailChecker 'ac-toulous.fr' ].freeze - def self.check(email:) + def check(email:) return { success: false } if email.blank? - parsed_email = Mail::Address.new(email) + parsed_email = Mail::Address.new(EmailSanitizableConcern::EmailSanitizer.sanitize(email)) return { success: false } if parsed_email.domain.blank? return { success: true } if KNOWN_DOMAINS.any? { _1 == parsed_email.domain } @@ -628,22 +631,22 @@ class EmailChecker private - def self.closest_domains(domain:) + def closest_domains(domain:) KNOWN_DOMAINS.filter do |known_domain| close_by_distance_of(domain, known_domain, distance: 1) || with_same_chars_and_close_by_distance_of(domain, known_domain, distance: 2) end end - def self.close_by_distance_of(a, b, distance:) + def close_by_distance_of(a, b, distance:) String::Similarity.levenshtein_distance(a, b) == distance end - def self.with_same_chars_and_close_by_distance_of(a, b, distance:) + def with_same_chars_and_close_by_distance_of(a, b, distance:) close_by_distance_of(a, b, distance: 2) && a.chars.sort == b.chars.sort end - def self.email_suggestions(parsed_email:, similar_domains:) + def email_suggestions(parsed_email:, similar_domains:) similar_domains.map { Mail::Address.new("#{parsed_email.local}@#{_1}").to_s } end end diff --git a/spec/lib/email_checker_spec.rb b/spec/lib/email_checker_spec.rb index cfcf73bfa..f9c35ea91 100644 --- a/spec/lib/email_checker_spec.rb +++ b/spec/lib/email_checker_spec.rb @@ -1,6 +1,6 @@ describe EmailChecker do describe 'check' do - subject { described_class } + subject { described_class.new } it 'works with identified use cases' do expect(subject.check(email: nil)).to eq({ success: false })