UNPKG

2.34 kBPlain TextView Raw
1import IUrlFetcher from './IUrlFetcher'
2
3import { NetworkError } from './errors'
4import persist from 'node-persist'
5import got from 'got'
6
7export const REQUEST_CACHE_DIR = '/tmp/dockter-request-cache'
8let REQUEST_CACHE_INITIALISED = false
9
10/**
11 * The default URL fetcher that Dockter uses. Fetches using `got` and caches results using `persist`
12 */
13export default class CachingUrlFetcher implements IUrlFetcher {
14 /**
15 * Fetch a URL using `got`, attempting to retrieve it from cache first.
16 */
17 async fetchUrl (url: string, options: any = { json: true }): Promise<any> {
18 let value = await CachingUrlFetcher.getFromCache(url)
19 if (value) return value
20
21 try {
22 const response = await got(url, options)
23 value = response.body
24 } catch (error) {
25 if (error.statusCode === 404) {
26 value = null
27 } else if (['ENOTFOUND', 'ECONNRESET', 'EAI_AGAIN', 'DEPTH_ZERO_SELF_SIGNED_CERT'].includes(error.code)) {
28 // These are usually connection errors
29 throw new NetworkError(`There was a problem fetching ${url} (${error.code}). Are you connected to the internet?`)
30 } else {
31 throw error
32 }
33 }
34
35 await CachingUrlFetcher.setToCache(url, value)
36
37 return value
38 }
39
40 /**
41 * Try to get a result from the `persist` cache. This method with initialise the `persist` cache if it has not been
42 * set up.
43 */
44 static async getFromCache (url: string): Promise<any> {
45 if (!REQUEST_CACHE_INITIALISED) {
46 await persist.init({
47 dir: REQUEST_CACHE_DIR,
48 ttl: 60 * 60 * 1000 // Milliseconds to cache responses for
49 })
50 REQUEST_CACHE_INITIALISED = true
51 }
52
53 try {
54 return await persist.getItem(url)
55 } catch (error) {
56 if (error.message.includes('does not look like a valid storage file')) {
57 // It seems that `persist.setItem` is not atomic and that the storage file can
58 // have zero bytes when we make multiple requests, some of which are for the same
59 // url. So we ignore this error and continue with the duplicate request.
60 return null
61 } else {
62 throw error
63 }
64 }
65 }
66
67 /**
68 * Set a value (URL response body, usually) to the `persist` cache.
69 */
70 static async setToCache (url: string, value: any): Promise<void> {
71 await persist.setItem(url, value)
72 }
73}