show retryable source outage guidance
This commit is contained in:
@@ -12,7 +12,9 @@ Bu beş adayın canlı `/api/documents/ilan` çağrıları da read-only denendi
|
|||||||
|
|
||||||
Belge URL’si, ilan metni ve tarayıcı belge metni endpoint’leri artık 502 durumunda `code: SOURCE_UNAVAILABLE` ve `retryable: true` alanlarını döndürüyor. İstemciler kaynak kesintisini kalıcı biçim hatasından ayırıp yeniden deneme sunabilir.
|
Belge URL’si, ilan metni ve tarayıcı belge metni endpoint’leri artık 502 durumunda `code: SOURCE_UNAVAILABLE` ve `retryable: true` alanlarını döndürüyor. İstemciler kaynak kesintisini kalıcı biçim hatasından ayırıp yeniden deneme sunabilir.
|
||||||
|
|
||||||
Doğrulama: 292/292 otomatik test geçti.
|
İhale detay istemcisi bu kodu tanıyıp “Resmi ihale kaynağı geçici olarak erişilemiyor; birkaç dakika sonra yeniden deneyin” mesajına dönüştürüyor; normal 503 gibi diğer durumlar özgün HTTP hatası olarak korunuyor.
|
||||||
|
|
||||||
|
Doğrulama: 292/292 otomatik test ve üretim derlemesi başarılı.
|
||||||
|
|
||||||
## Kanıtlanan ilk değişiklikler
|
## Kanıtlanan ilk değişiklikler
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,11 @@ export async function fetchTenderDetail(tenderId, {signal, timeoutMs = 30000} =
|
|||||||
const searchResult=String(tenderId).startsWith('ekap-');
|
const searchResult=String(tenderId).startsWith('ekap-');
|
||||||
const url=searchResult ? `/api/documents/ilan?ikn=${encodeURIComponent(tenderId.slice(5))}` : `/api/tenders/${encodeURIComponent(tenderId)}/detail`;
|
const url=searchResult ? `/api/documents/ilan?ikn=${encodeURIComponent(tenderId.slice(5))}` : `/api/tenders/${encodeURIComponent(tenderId)}/detail`;
|
||||||
const res = await fetch(url, {signal:controller.signal});
|
const res = await fetch(url, {signal:controller.signal});
|
||||||
if (!res.ok) throw new Error(`Detay API hatası: HTTP ${res.status}`);
|
const data = await res.json().catch(() => ({}));
|
||||||
const data = await res.json();
|
if (!res.ok) {
|
||||||
|
if (data.code === 'SOURCE_UNAVAILABLE' && data.retryable) throw new Error('Resmi ihale kaynağı geçici olarak erişilemiyor. Birkaç dakika sonra yeniden deneyin.');
|
||||||
|
throw new Error(`Detay API hatası: HTTP ${res.status}`);
|
||||||
|
}
|
||||||
if (!data.success) throw new Error(data.error || 'Detay alınamadı');
|
if (!data.success) throw new Error(data.error || 'Detay alınamadı');
|
||||||
if (searchResult) {
|
if (searchResult) {
|
||||||
if (data.ikn !== tenderId.slice(5)) throw new Error('Kaynak yanıtındaki İKN aranan ihaleyle uyuşmuyor.');
|
if (data.ikn !== tenderId.slice(5)) throw new Error('Kaynak yanıtındaki İKN aranan ihaleyle uyuşmuyor.');
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ test('detail loading rejects empty source, supports retry and stops on timeout/c
|
|||||||
assert.equal((await fetchTenderDetail('ilan-1')).aciklama,'Tam ilan metni');
|
assert.equal((await fetchTenderDetail('ilan-1')).aciklama,'Tam ilan metni');
|
||||||
globalThis.fetch=async()=>new Response('error',{status:503});
|
globalThis.fetch=async()=>new Response('error',{status:503});
|
||||||
await assert.rejects(()=>fetchTenderDetail('ilan-1'),/HTTP 503/);
|
await assert.rejects(()=>fetchTenderDetail('ilan-1'),/HTTP 503/);
|
||||||
|
globalThis.fetch=async()=>Response.json({success:false,code:'SOURCE_UNAVAILABLE',retryable:true,error:'kaynak yok'},{status:502});
|
||||||
|
await assert.rejects(()=>fetchTenderDetail('ilan-1'),/geçici olarak erişilemiyor.*yeniden deneyin/);
|
||||||
globalThis.fetch=async(url,{signal})=>new Promise((resolve,reject)=>{
|
globalThis.fetch=async(url,{signal})=>new Promise((resolve,reject)=>{
|
||||||
if(signal.aborted) reject(new DOMException('Cancelled','AbortError'));
|
if(signal.aborted) reject(new DOMException('Cancelled','AbortError'));
|
||||||
else signal.addEventListener('abort',()=>reject(new DOMException('Cancelled','AbortError')),{once:true});
|
else signal.addEventListener('abort',()=>reject(new DOMException('Cancelled','AbortError')),{once:true});
|
||||||
|
|||||||
Reference in New Issue
Block a user