پیکربندی
سه راه برای ساخت یک کلاینت، همهٔ گزینهها، و آنچه پیش از فرستادن درخواست رد میکند.
گزینهها
require "openemail" OpenEmail.init(api_key: ENV.fetch("OPENEMAIL_API_KEY"))OpenEmail.me.ping pinned = OpenEmail::Client.new(api_key: ENV.fetch("OPENEMAIL_API_KEY"), base_url: "https://api.openemail.uk")quick = OpenEmail::Client.new(ENV.fetch("OPENEMAIL_API_KEY"))billing = OpenEmail.create_client(api_key: ENV.fetch("BILLING_API_KEY")) p pinned.mode, quick.mode, billing.mode| نقطهٔ ورود | چه چیزی به شما میدهد |
|---|---|
| OpenEmail.init(...) | کلاینت مشترک را پیکربندی میکند و همان را برمیگرداند. از آن پس OpenEmail.client در هر فایل و هر ترد همان کلاینت است، و هر چیزی که ندهید از محیط خوانده میشود. |
| OpenEmail.client، OpenEmail.emails، OpenEmail.threads و هر فضای نام دیگر | کلاینت مشترک و میانبرهایی به فضاهای نام آن. اگر پیش از init استفاده شود، در نخستین فراخوانی خودش را از OPENEMAIL_API_KEY و OPENEMAIL_BASE_URL میسازد. |
| OpenEmail.reset_client | کلاینت مشترک را کنار میگذارد، پس فراخوانی بعدی یک کلاینت تازه از محیط میسازد. |
| OpenEmail.create_client(...) | کلاینتی جدا با همان بازگشت به محیط، برای کلیدی دوم در کنار کلید مشترک، یا برای کلاینتی که کد خودتان نگهش میدارد و دستبهدست میکند. |
| OpenEmail::Client.new(...) or OpenEmail::Client.new(api_key) | کلاینتی جدا که دقیقاً از آنچه میدهید ساخته میشود. هیچ چیزی از محیط نمیخواند، پس به api_key: یا access_token: نیاز دارد. OpenEmail.new همین فراخوانی است. |
OpenEmail.init( api_key: ENV.fetch("OPENEMAIL_API_KEY"), base_url: "https://api.openemail.uk", timeout: 30, max_retries: 2, adapter: OpenEmail::NetHttpAdapter.new(max_idle: 8, keep_alive_timeout: 2), headers: {"X-Team" => "billing"}, user_agent: "billing-service/1.4", disable_update_notice: true)| گزینه | پیشفرض | توضیحات |
|---|---|---|
| api_key: | OPENEMAIL_API_KEY | توسط init، create_client و کلاینت مشترک از محیط خوانده میشود. باید با oe_live_ یا oe_test_ آغاز شود. میتواند آرگومان نخست هم باشد، اما نه هر دو. |
| access_token: | OPENEMAIL_ACCESS_TOKEN | یک توکن دسترسی OAuth، یا هر چیزی که به call پاسخ دهد و توکنی برگرداند. بخش «توکنهای دسترسی OAuth» در پایین را ببینید. یک کلید یا یک توکن بدهید، هرگز هر دو را. |
| base_url: | https://api.openemail.uk | یا OPENEMAIL_BASE_URL. اسلشهای پایانی حذف میشوند، و init و create_client جلوی یک نام میزبان خالی https:// میگذارند، یا جلوی میزبانی روی همین دستگاه http://: localhost، یک نشانی 127.x.x.x یا ::1. اعتبارنامه هرگز با http ساده به میزبان دیگری فرستاده نمیشود، و 0.0.0.0 یا [::] هنگام ساختن کلاینت خطا raise میکند، چون اینها نشانیهاییاند که سرور روی آنها گوش میدهد، نه نشانیهایی برای فرستادن درخواست. |
| timeout: | 30 | ثانیه برای هر تلاش، نه برای هر فراخوانی. با آداپتور پیشفرض، برقراری اتصال و خواندن کل بدنه را در بر میگیرد، نه فقط سرآیندها را. 0 آن را خاموش میکند. files.upload دستکم 600 ثانیه صبر میکند، مگر آنکه روی همان فراخوانی timeout: بدهید. |
| max_retries: | 2 | تلاشهای اضافی پس از تلاش نخست، روی فراخوانیهایی که تکرارشان بیخطر است. روی کلاینت تنظیم میشود، نه برای هر فراخوانی. 0 تلاشهای دوباره را خاموش میکند. |
| adapter: | OpenEmail::NetHttpAdapter.new | لایهٔ HTTP. حالت پیشفرض برای هر میزبان تا 8 اتصال بیکار را هرکدام به مدت 2 ثانیه نگه میدارد، و max_idle: و keep_alive_timeout: این را تغییر میدهند. هر چیزی که به call(request) پاسخ دهد میتواند جای آن را بگیرد، و آزمون بدون شبکه همینطور اجرا میشود. |
| headers: | {} | با هر درخواست فرستاده میشود. |
| user_agent: | openemail-ruby/<version> | با هر درخواست فرستاده میشود. |
| disable_update_notice: | false | بررسی یکبار در هر فرایند برای یافتن نسخهٔ تازهتر روی RubyGems را رد میکند. این بررسی فقط وقتی اجرا میشود که خروجی استاندارد یک ترمینال باشد، و OPENEMAIL_DISABLE_UPDATE_NOTICE هم آن را خاموش میکند. |
متغیرهای محیطی
| متغیر | چه میکند |
|---|---|
| OPENEMAIL_API_KEY | کلیدی که init، create_client و کلاینت مشترک به کار میبرند، وقتی نه api_key: بدهید و نه access_token:. |
| OPENEMAIL_ACCESS_TOKEN | یک توکن دسترسی OAuth، که فقط وقتی خوانده میشود که هیچکدام از دو اعتبارنامه را ندهید و OPENEMAIL_API_KEY تنظیم نشده باشد، پس کلیدی که در محیط باشد مقدم است. |
| OPENEMAIL_BASE_URL | URL پایه، وقتی هیچ URLای ندهید. به میزبان خالی مانند localhost:2222 طرح نشانی (scheme) افزوده میشود. |
| OPENEMAIL_DISABLE_UPDATE_NOTICE | هر مقدار غیرخالی، اعلان بهروزرسانی را برای همهٔ کلاینتهای آن فرایند خاموش میکند. |
| HTTPS_PROXY و NO_PROXY، یا https_proxy و no_proxy | پراکسیای که آداپتور پیشفرض از طریق آن وصل میشود، و میزبانهایی که مستقیم وصل میشوند. بخش «پراکسیها» در پایین را ببینید. |
OpenEmail::Client.new هیچیک از سه مورد نخست را نمیخواند، پس کلاینتی که اینگونه ساخته شود هرگز تصادفاً کلیدی را از محیط برنمیدارد. متغیری که تنظیم شده اما خالی است، تنظیمنشده به حساب میآید.
پیش از ارسال چه چیزی را رد میکند
اینها ArgumentError را از همان خطی که مقدار نادرست در آن بوده raise میکنند، بهجای آنکه بهصورت شکستی گیجکننده در نخستین ارسال شما ظاهر شوند. پیام میگوید چه چیزی نادرست بوده و بهجایش چه باید داد، و هرگز اعتبارنامه را تکرار نمیکند.
| رد میشود | چرا |
|---|---|
| بدون هیچ اعتبارنامهای | نه api_key: داده شده و نه access_token:، و برای init و create_client هیچکدام از دو متغیر هم تنظیم نشده بود، پس چیزی برای احراز هویت نیست. هنگام ساختن کلاینت raise میشود. |
| یک کلید و یک توکن با هم | هر درخواست یک اعتبارنامه حمل میکند، پس کلاینت نمیتواند بفهمد منظورتان کدام بوده. کلیدی که هم بهعنوان آرگومان نخست و هم بهصورت api_key: داده شود، به همین دلیل رد میشود. |
| یک کوکی نشست، یک توکن نشست یا کلیدی برای سرویسی دیگر | تنها oe_live_ و oe_test_ اینجا احراز هویت میکنند، و API هم همین را میگوید. این بررسی فقط یک پیشوند است و نه بیشتر، پس کلید باطلشده همچنان روی سیم شکست میخورد، بهصورت یک OpenEmail::AuthenticationError. |
| base_url: ای که یک URL با http یا https نیست، یا URLای که نام کاربری یا گذرواژه در آن باشد | به چیز دیگری نمیتوان دسترسی یافت، و جای اعتبارنامه در api_key: یا access_token: است، نه در URL. هنگام ساختن کلاینت raise میشود. |
| اعتبارنامهای که با http ساده به میزبانی فرستاده شود که روی همین دستگاه نیست | توسط خود فراخوانی و پیش از فرستادن هر چیزی raise میشود. از یک URL پایه با https استفاده کنید. |
| timeout: ای که عددی برحسب ثانیه نیست، یا منفی است | تعداد ثانیهها را بدهید، یا 0 برای بدون مهلت. هنگام ساختن کلاینت raise میشود. |
| نام سرآیندی که یک token معتبر HTTP نیست، یا شکست خط در مقدار یک سرآیند | در headers:، user_agent: و idempotency_key: بررسی میشود، چون شکست خط یک سرآیند دوم را آغاز میکرد. |
| id خالی یا تماماً نقطه در هر متدی | هنگام فراخوانی متد raise میشود. بخشی از مسیر که فقط نقطه باشد را هر تجزیهگر URL حذف میکند، پس درخواست به اندپوینت دیگری میرسید. شناسهای که UTF-8 معتبر نباشد نیز رد میشود. |
| بدنهٔ درخواستی که Hash نیست | آرگومانهای کلیدواژهای یا یک Hash بدهید. هر چیزی که به to_hash پاسخ دهد، Hash به حساب میآید. |
گزینهای به نام test_mode: وجود ندارد و نخواهد داشت. طرح کلید بخشی از خودِ اعتبارنامه است نه یک اشاره، پس حالت، ویژگیِ کلید است. client.mode پیشوند را میخواند، "live" یا "test"، و دربارهٔ چیزی تصمیم نمیگیرد.
یک کلاینت، چند کلید
کلاینت را یک بار بسازید و به اشتراک بگذارید. کلاینت تازه برای هر درخواست، بیهیچ سودی اتصالهای باز خود را دور میریزد، و هیچیک از وضعیتهای روی آن مخصوص یک فراخواننده نیست. کلاینت پس از ساخته شدن منجمد (frozen) میشود و استفادهٔ همزمان از آن در چندین ترد بیخطر است، پس یک فرایند Puma یا Sidekiq فقط به یکی نیاز دارد، و پس از fork فرایند فرزند اتصالهای خودش را باز میکند.
برای موردی که در غیر این صورت به ازای هر کلید یک کلاینت لازم میکرد، مثلاً کاری که از طرف چند فضای کاری ارسال میکند، api_key: را روی همان فراخوانی بدهید. برای آن درخواست جایگزین سرآیند Authorization میشود و چیزی روی کلاینت باقی نمیگذارد.
message = {from: "[email protected]", to: "[email protected]", subject: "Your invoice", text: "Attached."}workspace_key = ENV.fetch("OPENEMAIL_API_KEY") client.emails.send(message) client.emails.send(message, api_key: workspace_key) client.threads.list(folder: "inbox", api_key: workspace_key)client.webhooks.list(api_key: workspace_key)هر متدی بیرون از temp_mail آن را بهصورت آرگومان کلیدواژهای میگیرد، روی یک فهرست در کنار فیلترها، و متدهای temp_mail بهجای آن inbox_token: میگیرند. پیش از فرستادن درخواست و با همان قاعدهای که کلاینت به کار میبرد بررسی میشود، پس یک غلط تایپی بهجای 401 دربارهٔ اعتبارنامهای که باید بگردید و پیدایش کنید، یک ArgumentError دربارهٔ api_key دادهشده به این فراخوانی raise میکند. فراخوانیای که دوباره تلاش میشود همان کلیدی را نگه میدارد که به آن داده شده بود.
client.mode کلیدی را توصیف میکند که کلاینت در زمان ساخت با آن ساخته شده، و از بازنویسیهای موردی پیروی نمیکند. وقتی یک کلاینت به چند کلید خدمت میکند دیگر یک حالت یکتا برای گزارش وجود ندارد، پس آن را از روی کلیدی که دادهاید بخوانید. client.inspect حالت و URL پایه را نشان میدهد، هرگز کلید را.
اندپوینتهایی که هیچ متدی آنها را نمیپوشاند
client.raw لایهٔ انتقالی است که هر متد از آن عبور میکند. client.raw.request مسیری را فراخوانی میکند که هنوز هیچ متدی آن را نمیپوشاند، با اعمال اعتبارنامه، URL پایه، مهلت و سیاست تلاش دوبارهٔ کلاینت، و بدنهٔ تجزیهشده را همانگونه برمیگرداند که یک متد برمیگرداند.
ping = client.raw.request("/ping") label = client.raw.request("/labels", method: :post, body: {name: "Invoices"}) p ping[:ok], label[:id]| کلیدواژه | چه میکند |
|---|---|
| method: | :get، مگر آنکه چیز دیگری بگویید: :post، :put، :patch یا :delete. |
| query: | یک Hash از پارامترهای کوئری. مقدارهای nil و خالی کنار گذاشته میشوند، یک Array یا Set با کاما به هم وصل میشود، و یک Time بهصورت یک لحظهٔ ISO 8601 فرستاده میشود. |
| body: | یک Hash که بهصورت JSON فرستاده میشود. |
| raw: و content_type: | بایتهایی که همانطور که هستند فرستاده میشوند، بهصورت یک String دودویی، یک IO یا یک Pathname، با application/octet-stream مگر آنکه نوعی را نام ببرید. |
| accept: و binary: | accept: ای غیر از JSON بدنه را بهصورت متن برمیگرداند، و binary: true آن را بهصورت یک String دودویی برمیگرداند. |
| idempotent: و idempotency_key: | idempotent: true یک Idempotency-Key میچسباند که تولید میشود، مگر آنکه کلید خودتان را بدهید. |
| repeatable: | اینکه آیا یک شکست دوباره تلاش میشود یا نه. فقط GET دوباره تلاش میشود، مگر آنکه repeatable: true بدهید. |
| api_key: و timeout: | همان کلید مخصوص هر فراخوانی، و مهلتی برحسب ثانیه فقط برای همین فراخوانی. |
مسیر باید با یک / تنها آغاز شود، و مسیری که URL نهاییاش از origin مربوط به URL پایه بیرون برود، پیش از فرستادن هر چیزی ArgumentError را raise میکند، پس اعتبارنامه هرگز به میزبان دیگری نمیرسد.
صندوقهای یکبارمصرف
OpenEmail.create_temp_mail کلاینتی برای صندوقهای یکبارمصرف میسازد که هیچ کلید API حمل نمیکند و هیچ کلیدی هم از محیط نمیخواند. صندوقها را ناشناس میسازد، و هر خواندن توکن صندوقی را میفرستد که create بازگردانده بود، یا توکن تازهتری را که extend بازگرداند، خواه برای هر فراخوانی بهصورت inbox_token: خواه یک بار بهصورت OpenEmail.create_temp_mail(inbox_token:).
temp_mail = OpenEmail.create_temp_mail inbox = temp_mail.createpage = temp_mail.list_messages(inbox[:id], inbox_token: inbox[:token]) p page.items.size, page.expires_atcreate_temp_mail مانند هر کلاینتی base_url:، adapter:، max_retries:، timeout:، user_agent:، headers: و disable_update_notice: را میگیرد، و وقتی هیچ URL پایهای ندهید OPENEMAIL_BASE_URL را میخواند.
توکنهای دسترسی OAuth
برنامهای که شخصی با OAuth متصل کرده، مثل یک ابزار خط فرمان یا یک عامل، بهجای کلید API یک توکن دسترسی دارد. آن را بهصورت access_token: بدهید: یا خودِ توکن، یا هر چیزی که به call پاسخ دهد و توکن را برگرداند، مانند یک lambda یا یک Method. برای هر فراخوانی یک بار صدا زده میشود و تلاشهای دوبارهٔ همان فراخوانی از چیزی که برگردانده استفاده میکنند، پس وقتی توکن نزدیک انقضاست آن را درون همان callable تازه کنید تا هرگز لازم نباشد کلاینت را از نو بسازید.
tokens = {current: "token-from-your-oauth-flow"} oauth_client = OpenEmail::Client.new(access_token: -> { tokens.fetch(:current) }) me = oauth_client.me.get puts me[:clientId], me[:expiresAt] if me[:object] == "oauth_token"| حالت | چه رخ میدهد |
|---|---|
| api_key: و access_token: با هم، یا هیچکدام | کلاینت هنگام ساخته شدن ArgumentError را raise میکند. وقتی هیچکدام نباشد، پیام OPENEMAIL_API_KEY و OPENEMAIL_ACCESS_TOKEN را نام میبرد. |
| مقداری که توکن نیست | توکن 1 تا 512 نویسه است و با oe_ شروع نمیشود، همان بررسیای که OpenEmail.access_token? انجام میدهد. Stringای که از این بررسی رد نشود هنگام ساختن کلاینت خطا raise میکند، و callableی که چنین مقداری برگرداند، پیش از فرستادن هر چیزی از درون فراخوانی ArgumentError را raise میکند. |
| OPENEMAIL_ACCESS_TOKEN | وقتی هیچکدام از دو اعتبارنامه را ندهید و OPENEMAIL_API_KEY تنظیم نشده باشد، init، create_client و کلاینت مشترک آن را میخوانند، پس کلیدی که در محیط باشد مقدم است. |
| callableی که خطا raise کند | فراخوانی همان خطا را، بیتغییر، raise میکند و چیزی فرستاده نمیشود. |
| یک api_key: مخصوص یک فراخوانی | فقط برای همان یک درخواست جای توکن را میگیرد و callable صدا زده نمیشود. |
| client.mode | با توکن همیشه "live" است. |
| OpenEmail.create_temp_mail | هر چه در محیط باشد، هیچ اعتباری نمیفرستد. |
| me.get و me.ping | برای توکن، get با object برابر oauth_token، id و roleId برابر nil، clientId برنامهٔ متصل، و expiresAt، یعنی زمانی که تأیید شخص برای برنامه تمام میشود، پاسخ میدهد. ping با kind برابر oauth، keyId برابر nil و clientId پاسخ میدهد. پیش از خواندن id یا keyId، مقدار object یا kind را بررسی کنید. |
توکن از طرف یک شخص کار میکند و ایمیلش را همانطور که خودش میتواند میخواند، پس آن را مثل کلید روی سرور نگه دارید.
کدهای تأیید هویت
پیش از یک تغییر حساس، مثل حذف یک دامنه یا تغییر یک وبهوک، API از توکن دسترسی همان کد تأیید هویتی را میخواهد که برنامهٔ وب از شخص میخواست. فراخوانی یک OpenEmail::PermissionError را raise میکند، یک 403 که step_up_required? آن true است، و چیزی تغییر نکرده است. یک کد بخواهید، کدی را که شخص به شما میدهد تأیید کنید، سپس دوباره فراخوانی کنید. از کلید API هرگز خواسته نمیشود.
domain_id = "b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f" begin client.domains.delete(domain_id)rescue OpenEmail::ApiError => error raise unless error.step_up_required? challenge = client.security.begin_step_up if challenge[:method] == "email" puts "Enter the code we emailed to #{challenge[:sentTo]}" else puts "Enter the code from your authenticator app, or a backup code" end client.security.verify_step_up(code: $stdin.gets.to_s.strip) client.domains.delete(domain_id)end| متد | چه میکند |
|---|---|
| security.step_up_status | اینکه برنامه همین حالا تأییدشده است یا نه (elevated، elevatedUntil)، کد بعدی چطور بررسی میشود (method، email یا totp)، و minutes، طول بازهٔ زمانی. چیزی نمیفرستد و توقف را گزارش نمیکند. |
| security.begin_step_up | یک تأیید هویت را آغاز میکند. با email یک کد ششرقمی به نشانیای میرود که شخص با آن وارد میشود و sentTo آن را پوشیده نشان میدهد. با totp شخص کدی را از برنامهٔ احراز هویتش میخواند یا یک کد بازیابی به کار میبرد. تأیید هویتی که هنوز باز است و تلاش باقی دارد دوباره به کار میرود، مگر اینکه resend: true بدهید، و تأیید هویت قفلشده یا منقضیشده با یک فراخوانی ساده جایگزین میشود. هر برنامه برای هر شخص میتواند 5 تأیید هویت در ساعت و 20 در 24 ساعت آغاز کند، و بعدی یک 429 step_up_throttled را raise میکند. |
| security.verify_step_up(code:) | کد را بررسی میکند و تغییرات حساس را برای این برنامه به مدت 60 دقیقه، تا elevatedUntil، از راه REST و از راه ابزارهای MCP که همان تغییرها را انجام میدهند باز میکند. پس از 10 کد نادرست در 24 ساعت از این برنامه، یا 20 کد از همهٔ برنامههای شخص با هم، این فراخوانی و begin_step_up یک 429 step_up_locked را با پیامی raise میکنند که میگوید تأیید هویت کی از سر گرفته میشود. |
کلاینت هرگز خودش کد نمیخواهد یا فراخوانی را تکرار نمیکند، و هیچکدام از این سه متد خودکار دوباره تلاش نمیشود، چون تلاش دوباره پس از یک پاسخ گمشده ممکن است ایمیل دومی بفرستد یا تلاش دومی را مصرف کند. اسکوپ لازم ندارند، و کلید API که یکی از آنها را فراخوانی کند یک 400 step_up_not_applicable میگیرد. OpenEmail::STEP_UP_ERROR_CODES همهٔ راههایی را که تأیید ممکن است شکست بخورد نام میبرد، و صفحهٔ خطاهای API میگوید برای هر کدام چه باید کرد.
اعلان بهروزرسانی
وقتی نسخهٔ تازهتری از gem روی RubyGems باشد، کلاینت این را یک بار در هر فرایند، روی خروجی خطای استاندارد، با خطی مانند ℹ openemail 0.0.2 is available, you are on 0.0.1. و به دنبالش صفحهٔ gem اعلام میکند. این بررسی هنگام ساخته شدن نخستین کلاینت، در یک ترد پسزمینه با مهلت دو ثانیه، و فقط وقتی خروجی استاندارد یک ترمینال باشد اجرا میشود، و ناتوانی در دسترسی به RubyGems نادیده گرفته میشود.
این بررسی از آداپتور کلاینت عبور میکند، پس وقتی آزمونها در ترمینال اجرا شوند، یک آداپتور آزمونی ممکن است درخواستی به RubyGems ببیند. کلاینتهای آزمونی را با disable_update_notice: true بسازید، یا OPENEMAIL_DISABLE_UPDATE_NOTICE را تنظیم کنید.
پراکسیها
آداپتور پیشفرض پراکسی خود را با URI#find_proxy خودِ Ruby پیدا میکند، پس از همان قواعد بقیهٔ کتابخانهٔ استاندارد پیروی میکند: https_proxy یا HTTPS_PROXY پراکسی را نام میبرد، و no_proxy یا NO_PROXY میزبانهایی را فهرست میکند که مستقیم وصل میشوند. نام کاربری و گذرواژهٔ درون URL پراکسی به پراکسی فرستاده میشوند، و به سروری که روی همین دستگاه است هرگز از راه پراکسی وصل نمیشود.
اتصالها از TLS 1.2 یا بالاتر استفاده میکنند و گواهی سرور را بررسی میکنند، پس پراکسیای که TLS را بازرسی میکند لازم است مرجع صدور گواهیاش مورد اعتماد OpenSSL روی آن دستگاه باشد.
آزمون بدون شبکه
adapter: جای لایهٔ HTTP را میگیرد. هر چیزی است که به call(request) پاسخ دهد، از جمله یک lambda، و یک OpenEmail::HttpResponse با status، headers و body برگرداند. درخواست یک OpenEmail::HttpRequest با method، url، headers، body و timeout است، پس آزمون میتواند دقیقاً بررسی کند چه چیزی بیرون میرفت.
requests = [] adapter = lambda do |request| requests << request OpenEmail::HttpResponse.new( status: 200, headers: {"content-type" => "application/json"}, body: JSON.generate({id: "msg_test", status: "sent", replayed: false}) )end test_client = OpenEmail::Client.new(api_key: "oe_test_fake", adapter:, max_retries: 0, disable_update_notice: true) sent = test_client.emails.send(from: "[email protected]", to: "[email protected]", subject: "Hi", text: "Hello") p sent[:status], requests.first.method, requests.first.url, requests.first.headers["Idempotency-Key"]p requests.firstچاپ یک درخواست، سرآیند Authorization آن را بهصورت [redacted] نشان میدهد، پس گزارش آزمون هرگز کلید را در خود ندارد.
- وضعیتی بیرون از 2xx را با پاکت خطای API بهعنوان بدنه برگردانید، مانند
{"error": {"type": "validation_error", "code": "invalid_parameter", "message": "..."}}، تا زیرکلاس متناظرOpenEmail::ApiErrorرا بگیرید. - از
callیکTimeout::Errorرا raise کنید، یاNet::ReadTimeoutرا که خودش از همین نوع است، تا یکOpenEmail::NetworkErrorبگیرید کهtimeout?آن true است. هر StandardError دیگری، مانندErrno::ECONNREFUSED، به یکNetworkErrorتبدیل میشود کهtimeout?آن false است. NameError،TypeErrorوArgumentErrorکه درون آداپتور raise شوند، باگهای خود آداپتور به حساب میآیند. بیتغییر raise میشوند و هرگز دوباره تلاش نمیشوند.
وقتی شکستها را در آزمون شبیهسازی میکنید، کلاینت آزمونی را با max_retries: 0 بسازید. در غیر این صورت، یک وضعیت قابل تلاش دوباره یا یک شکست شبکه روی فراخوانیای که تکرارش بیخطر است سه بار امتحان میشود، با وقفههای واقعی در میانشان.