Skip to content

Commit 1196ecb

Browse files
author
Nikos Vasileiou
committed
Add fetch content timeout parameters
1 parent f617f75 commit 1196ecb

File tree

3 files changed

+135
-11
lines changed

3 files changed

+135
-11
lines changed

packages/native/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@ tx.init({
153153

154154
// Translation cache, defaults to "new MemoryCache()"
155155
cache: Function,
156+
157+
// Optional timeout in milliseconds when fetching languages and
158+
// strings, defaults to 0 (no-timeout)
159+
fetchTimeout: Number,
160+
161+
// Optional interval polling delay in milliseconds while waiting
162+
// for CDS to warm-up with content, defaults to 250msec
163+
fetchInterval: Number,
156164
})
157165
```
158166

packages/native/src/TxNative.js

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export default class TxNative {
3030
this.token = '';
3131
this.secret = '';
3232
this.filterTags = '';
33+
this.fetchTimeout = 0;
34+
this.fetchInterval = 250;
3335
this.cache = new MemoryCache();
3436
this.missingPolicy = new SourceStringPolicy();
3537
this.errorPolicy = new SourceErrorPolicy();
@@ -48,6 +50,8 @@ export default class TxNative {
4850
* @param {String} params.filterTags
4951
* @param {String} params.token
5052
* @param {String} params.secret
53+
* @param {Number} params.fetchTimeout
54+
* @param {Number} params.fetchInterval
5155
* @param {Function} params.cache
5256
* @param {Function} params.missingPolicy
5357
* @param {Function} params.errorPolicy
@@ -61,6 +65,8 @@ export default class TxNative {
6165
'secret',
6266
'cache',
6367
'filterTags',
68+
'fetchTimeout',
69+
'fetchInterval',
6470
'missingPolicy',
6571
'errorPolicy',
6672
'stringRenderer',
@@ -183,11 +189,17 @@ export default class TxNative {
183189
}
184190
}
185191

192+
const handleError = (err) => {
193+
sendEvent(TRANSLATIONS_FETCH_FAILED, { localeCode, filterTags }, this);
194+
return err;
195+
};
196+
186197
// contact CDS
187198
try {
188199
sendEvent(FETCHING_TRANSLATIONS, { localeCode, filterTags }, this);
189200
let response;
190201
let lastResponseStatus = 202;
202+
const tsNow = Date.now();
191203
while (lastResponseStatus === 202) {
192204
/* eslint-disable no-await-in-loop */
193205
let url = `${this.cdsHost}/content/${localeCode}`;
@@ -201,8 +213,14 @@ export default class TxNative {
201213
'X-NATIVE-SDK': `txjs/${__PLATFORM__}/${__VERSION__}`,
202214
},
203215
});
204-
/* eslint-enable no-await-in-loop */
205216
lastResponseStatus = response.status;
217+
if (this.fetchTimeout > 0 && (Date.now() - tsNow) >= this.fetchTimeout) {
218+
throw handleError(new Error('Fetch translations timeout'));
219+
}
220+
if (lastResponseStatus === 202 && this.fetchInterval > 0) {
221+
await sleep(this.fetchInterval);
222+
}
223+
/* eslint-enable no-await-in-loop */
206224
}
207225

208226
const { data } = response;
@@ -216,12 +234,10 @@ export default class TxNative {
216234
this.cache.update(localeCode, hashmap);
217235
sendEvent(TRANSLATIONS_FETCHED, { localeCode, filterTags }, this);
218236
} else {
219-
sendEvent(TRANSLATIONS_FETCH_FAILED, { localeCode, filterTags }, this);
220-
throw new Error('Could not fetch translations');
237+
throw handleError(new Error('Could not fetch translations'));
221238
}
222239
} catch (err) {
223-
sendEvent(TRANSLATIONS_FETCH_FAILED, { localeCode, filterTags }, this);
224-
throw err;
240+
throw handleError(err);
225241
}
226242
}
227243

@@ -355,11 +371,17 @@ export default class TxNative {
355371

356372
if (!this.token) return [];
357373

374+
const handleError = (err) => {
375+
sendEvent(LOCALES_FETCH_FAILED, null, this);
376+
return err;
377+
};
378+
358379
// contact CDS
359380
try {
360381
sendEvent(FETCHING_LOCALES, null, this);
361382
let response;
362383
let lastResponseStatus = 202;
384+
const tsNow = Date.now();
363385
while (lastResponseStatus === 202) {
364386
/* eslint-disable no-await-in-loop */
365387
response = await axios.get(`${this.cdsHost}/languages`, {
@@ -369,8 +391,14 @@ export default class TxNative {
369391
'X-NATIVE-SDK': `txjs/${__PLATFORM__}/${__VERSION__}`,
370392
},
371393
});
372-
/* eslint-enable no-await-in-loop */
373394
lastResponseStatus = response.status;
395+
if (this.fetchTimeout > 0 && (Date.now() - tsNow) >= this.fetchTimeout) {
396+
throw handleError(new Error('Get locales timeout'));
397+
}
398+
if (lastResponseStatus === 202 && this.fetchInterval > 0) {
399+
await sleep(this.fetchInterval);
400+
}
401+
/* eslint-enable no-await-in-loop */
374402
}
375403

376404
const { data } = response;
@@ -379,12 +407,10 @@ export default class TxNative {
379407
this.locales = this.languages.map((entry) => entry.code);
380408
sendEvent(LOCALES_FETCHED, null, this);
381409
} else {
382-
sendEvent(LOCALES_FETCH_FAILED, null, this);
383-
throw new Error('Could not fetch languages');
410+
throw handleError(new Error('Could not fetch languages'));
384411
}
385412
} catch (err) {
386-
sendEvent(LOCALES_FETCH_FAILED, null, this);
387-
throw err;
413+
throw handleError(err);
388414
}
389415

390416
return [...this.locales];

packages/native/tests/tx.test.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
1-
/* globals describe, it */
1+
/* globals describe, it, beforeEach, afterEach */
22

33
import { expect } from 'chai';
44
import nock from 'nock';
55
import { tx, t, generateKey } from '../src/index';
66

77
describe('tx instance', () => {
8+
let fetchTimeout;
9+
let fetchInterval;
10+
11+
beforeEach(() => {
12+
fetchTimeout = tx.fetchTimeout;
13+
fetchInterval = tx.fetchInterval;
14+
tx.init({ fetchTimeout: 0, fetchInterval: 0 });
15+
});
16+
17+
afterEach(() => {
18+
nock.cleanAll();
19+
tx.init({ fetchTimeout, fetchInterval });
20+
});
21+
822
it('getLocales fetches locales', async () => {
923
tx.init({
1024
token: 'abcd',
@@ -216,6 +230,51 @@ describe('tx instance', () => {
216230
expect(locales).to.deep.equal(['el']);
217231
});
218232

233+
it('retries fetching languages with timeout', async () => {
234+
tx.init({ token: 'abcd', fetchTimeout: 50 });
235+
nock(tx.cdsHost)
236+
.get('/languages')
237+
.delayConnection(60)
238+
.reply(202)
239+
.get('/languages')
240+
.reply(200, {
241+
data: [{
242+
name: 'Greek',
243+
code: 'el',
244+
localized_name: 'Ελληνικά',
245+
rtl: false,
246+
}],
247+
});
248+
249+
let error;
250+
try {
251+
await tx.getLocales({ refresh: true });
252+
} catch (err) {
253+
error = err;
254+
}
255+
expect(error.message).to.equal('Get locales timeout');
256+
});
257+
258+
it('retries fetching languages with interval', async () => {
259+
tx.init({ token: 'abcd', fetchInterval: 50 });
260+
nock(tx.cdsHost)
261+
.get('/languages')
262+
.reply(202)
263+
.get('/languages')
264+
.reply(200, {
265+
data: [{
266+
name: 'Greek',
267+
code: 'el',
268+
localized_name: 'Ελληνικά',
269+
rtl: false,
270+
}],
271+
});
272+
273+
const ts = Date.now();
274+
await tx.getLocales({ refresh: true });
275+
expect(Date.now() - ts).to.be.greaterThan(50);
276+
});
277+
219278
it('retries fetching translations', async () => {
220279
tx.init({ token: 'abcd' });
221280
nock(tx.cdsHost)
@@ -227,4 +286,35 @@ describe('tx instance', () => {
227286
await tx.fetchTranslations('el');
228287
expect(tx.cache.get('source', 'el')).to.equal('translation');
229288
});
289+
290+
it('retries fetching translations with timeout', async () => {
291+
tx.init({ token: 'abcd', fetchTimeout: 50 });
292+
nock(tx.cdsHost)
293+
.get('/content/el_timeout')
294+
.delayConnection(60)
295+
.reply(202)
296+
.get('/content/el_timeout')
297+
.reply(200, { data: { source: { string: 'translation' } } });
298+
299+
let error;
300+
try {
301+
await tx.fetchTranslations('el_timeout');
302+
} catch (err) {
303+
error = err;
304+
}
305+
expect(error.message).to.equal('Fetch translations timeout');
306+
});
307+
308+
it('retries fetching translations with interval delays', async () => {
309+
tx.init({ token: 'abcd', fetchInterval: 50 });
310+
nock(tx.cdsHost)
311+
.get('/content/el_interval')
312+
.reply(202)
313+
.get('/content/el_interval')
314+
.reply(200, { data: { source: { string: 'translation' } } });
315+
316+
const ts = Date.now();
317+
await tx.fetchTranslations('el_interval');
318+
expect(Date.now() - ts).to.be.greaterThan(50);
319+
});
230320
});

0 commit comments

Comments
 (0)