Aller à la documentation
Ruby

Erreurs

Chaque échec lève une erreur. Une classe pour un refus, une pour l'absence de réponse, et un request id sur chaque erreur d'API.

En attraper une

rescue_errors.rb
message = {from: "[email protected]", to: "[email protected]", subject: "Your September invoice", text: "Invoice attached."} begin  client.emails.send(message)rescue OpenEmail::ApiError => error  warn "#{error.code} #{error.param} #{error.message}" if error.validation?  warn client.addresses.list_all.addresses.inspect if error.permission?  warn "try again in #{error.retry_after_seconds} seconds" if error.rate_limited?   warn "#{error.status} #{error.request_id}"  raiserescue OpenEmail::NetworkError => error  warn "no answer in time" if error.timeout?  raiseend

Un permission_error sur un envoi vient généralement de la portée d'envoi de la CLÉ, un domaine ou une adresse qui ne lui a pas été accordé, plutôt que de l'espace de travail, et c'est pourquoi l'exemple affiche les adresses depuis lesquelles addresses.list_all indique que cette clé peut envoyer.

Chaque type de refus a sa propre sous-classe : un rescue peut donc choisir par classe ceux qu'il traite et laisser remonter les autres.

rescue_by_class.rb
message = {from: "[email protected]", to: "[email protected]", subject: "Your September invoice", text: "Invoice attached."} begin  client.emails.send(message)rescue OpenEmail::ValidationError => error  warn "#{error.param}: #{error.message}"rescue OpenEmail::AuthenticationError, OpenEmail::PermissionError => error  warn "the key cannot do this: #{error.code}"  raiserescue OpenEmail::Error => error  warn "#{error.class}: #{error.message}"  raiseend

Les classes

ClasseQuand
OpenEmail::ErrorLa base de toutes les erreurs que définit la gem : rescue OpenEmail::Error les intercepte donc toutes. Elle n'intercepte pas ArgumentError.
OpenEmail::ApiErrorL'API a répondu, et pas par un succès. Porte status, type, code, param, doc_url, request_id, retry_after_seconds, fields et body. Levée telle quelle quand type vaut api_error, comme pour une panne du serveur, et sous la sous-classe de son type sinon.
OpenEmail::InvalidRequestError, AuthenticationError, PermissionError, NotFoundError, ConflictError, ValidationError et RateLimitErrorSous-classes d'ApiError, une pour chaque type : invalid_request_error, authentication_error, permission_error, not_found_error, conflict_error, validation_error et rate_limit_error.
OpenEmail::NetworkErrorAucune réponse n'est arrivée : DNS, TLS, une connexion refusée ou coupée, ou le délai dépassé. Porte original, l'exception sous-jacente, qui est aussi sa cause, et timeout? vaut true quand le délai était la raison.
OpenEmail::WebhookSignatureErrorOpenEmail.verify_webhook_signature a refusé une livraison.
ArgumentErrorLevée avant tout envoi : une clé manquante ou mal formée, une base_url: inutilisable, un id vide. C'est la classe Ruby ordinaire, pas une OpenEmail::Error, car elle signifie que l'appel lui-même est erroné.

Ce que porte une ApiError

messageString
La phrase de l'API elle-même, écrite pour un humain, qui nomme la valeur fautive quand il y en a une. Ce n'est pas un identifiant stable : branchez-vous donc sur `code`.
statusInteger
Le statut HTTP de la réponse.
typeString
L'une des huit valeurs de `OpenEmail::ERROR_TYPES`, un ensemble figé qui ne s'agrandira pas. Quand le corps n'en nomme aucune, elle est déduite du statut.
codeString
L'échec précis, comme `from_address_forbidden` ou `invalid_email_address`. Ouvert et additif : traitez donc un code que vous ne reconnaissez pas comme son `type`. Il vaut `unrecognised_response` quand le corps n'était pas l'enveloppe d'erreur de l'API.
paramString or nil
Le champ refusé, sous forme de chemin pointé comme `to.0`, quand l'échec en nomme un.
doc_urlString or nil
Une page sur cet échec, quand l'API en nomme une.
request_idString or nil
L'id sous lequel le serveur a journalisé la requête, tiré du corps ou de l'en-tête `x-request-id`.
retry_after_secondsInteger, Float or nil
L'attente demandée par le serveur dans `Retry-After`, en secondes, qu'il ait envoyé un nombre ou une date. nil quand il n'en a envoyé aucune.
fieldsArray<Hash> or nil
Un Hash par problème, chacun avec `key` et `error`, comme `{key: "email", error: "email"}` quand `forms.subscribe` a refusé les réponses avec un 422 `invalid_form_submission`. nil quand l'erreur n'en liste aucun.
bodyHash or nil
La réponse d'erreur entière, analysée, avec des clés Symbol. nil quand elle était vide ou n'était pas du JSON.
PrédicatVrai quand
auth?type vaut authentication_error, un 401 : aucune clé, un type d'identifiant erroné, ou une clé que nous n'avons pas émise.
permission?permission_error, un 403 : une vraie clé à laquelle manque la portée ou l'adresse From dont elle a besoin.
scope_missing?code vaut insufficient_scope, le 403 qui nomme une portée manquante.
invalid_request?invalid_request_error, un 400 : une requête qui n'a pas pu être comprise. Un message dépassant le plafond de taille revient en 422 message_too_large : c'est donc le prédicat validation? qui l'attrape.
validation?validation_error, un 422 : le schéma l'a refusée, et param nomme le champ.
not_found?not_found_error, un 404 : la ressource n'existe pas.
conflict?conflict_error, un 409 : la ressource a dépassé le stade où cette opération pouvait encore lui être appliquée.
rate_limited?rate_limit_error, un 429. retry_after_seconds contient le délai d'attente quand le serveur en a indiqué un.
server_error?status vaut 500 ou plus. Citez request_id si vous contactez le support.
retryable?status vaut 408, 429, 500, 502, 503 ou 504.
step_up_required?code vaut step_up_required, le 403 qu'un jeton d'accès OAuth reçoit avant un changement sensible tant que la personne n'a pas vérifié de code.

La plupart des prédicats lisent type, la moitié figée de l'enveloppe, et chaque sous-classe correspond à un type. code reste une String, car l'API garantit qu'il est ouvert et additif : traitez donc un code que vous ne reconnaissez pas comme son type. Une liste fermée ferait d'une mise à jour de la gem le prix à payer pour lire un nouveau mode d'échec.

Un corps qui n'est pas l'enveloppe d'erreur de l'API lève malgré tout une OpenEmail::ApiError, avec type déduit du statut et code fixé à unrecognised_response. Une réponse en succès dont le corps n'est pas du JSON en lève une également.

retryable? décrit le statut, pas votre appel. Un appel qui peut être répété sans risque a déjà été réessayé au moment où il lève l'erreur, et un 429 comme send_quota_exceeded ou ai_quota_exceeded échoue de la même façon tant que son quota n'est pas réinitialisé : montrez-le donc à une personne plutôt que de boucler dessus.

Une exception levée par votre adaptateur devient une OpenEmail::NetworkError une fois épuisés les réessais que permet l'appel, avec l'originale dans original et cause. NameError, TypeError et ArgumentError font exception : elles signalent un bug dans l'adaptateur, et sont donc relevées telles quelles et jamais réessayées.

request_id

Chaque OpenEmail::ApiError porte l'id de requête envoyé par le serveur, issu du corps de l'erreur ou de l'en-tête x-request-id, et c'est la seule chose qui relie votre échec à une ligne du journal du serveur. Une réponse en succès renvoie le seul corps analysé : il n'y a donc pas d'id de requête à y lire.