Skip to content

Commit 39fb618

Browse files
committed
add basic cache, using memory for now (cov 94%)
1 parent 68d5aad commit 39fb618

File tree

2 files changed

+123
-58
lines changed

2 files changed

+123
-58
lines changed

index.js

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ const http = require('http')
44
const fetch = require('node-fetch')
55

66
class Updates {
7-
constructor ({ token } = {}) {
7+
constructor ({ token, cache = new MemoryCache() } = {}) {
88
this.token = token
9+
this.cache = cache
910
}
11+
1012
listen (port, cb) {
1113
if (typeof port === 'function') {
1214
;[port, cb] = [undefined, port]
@@ -41,21 +43,13 @@ class Updates {
4143
}
4244

4345
async handleReleases (res, account, repository) {
44-
const latest = await this.getLatest(account, repository, 'win32')
45-
if (!latest) return notFound(res)
46-
47-
const url = `https://github.com/${account}/${repository}/releases/download/${
48-
latest.version
49-
}/RELEASES`
50-
const rres = await fetch(url)
51-
const body = await rres.text()
52-
const matches = body.match(/[^ ]*\.nupkg/gim)
53-
const nuPKG = url.replace('RELEASES', matches[0])
54-
res.end(body.replace(matches[0], nuPKG))
46+
const latest = await this.cachedGetLatest(account, repository, 'win32')
47+
if (!latest || !latest.RELEASES) return notFound(res)
48+
res.end(latest.RELEASES)
5549
}
5650

5751
async handleUpdate (res, account, repository, platform, version) {
58-
const latest = await this.getLatest(account, repository, platform)
52+
const latest = await this.cachedGetLatest(account, repository, platform)
5953

6054
if (!latest) {
6155
notFound(res)
@@ -70,11 +64,30 @@ class Updates {
7064
}
7165
}
7266

67+
async cachedGetLatest (account, repository, platform) {
68+
const key = `${account}/${repository}/${platform}`
69+
let latest = await this.cache.get(key)
70+
if (latest) {
71+
this.log(`cache hit ${key}`)
72+
return latest.version ? latest : null
73+
}
74+
75+
latest = await this.getLatest(account, repository, platform)
76+
if (latest) {
77+
await this.cache.set(key, latest)
78+
return latest
79+
} else {
80+
await this.cache.set(key, {})
81+
return null
82+
}
83+
}
84+
7385
async getLatest (account, repository, platform) {
7486
const url = `https://api.github.com/repos/${account}/${repository}/releases?per_page=100`
7587
const headers = { Accept: 'application/vnd.github.preview' }
7688
if (this.token) headers.Authorization = `token ${this.token}`
7789
const res = await fetch(url, { headers })
90+
this.log(`API github releases status=${res.status}`)
7891

7992
if (res.status === 403) {
8093
console.error('Rate Limited!')
@@ -85,19 +98,50 @@ class Updates {
8598
return
8699
}
87100

101+
let latest
102+
88103
const releases = await res.json()
89104
for (const release of releases) {
90105
if (release.draft || release.prerelease) continue
91106
for (const asset of release.assets) {
92107
if (assetPlatform(asset.name) === platform) {
93-
return {
108+
latest = {
94109
version: release.name || release.tag_name,
95110
url: asset.browser_download_url,
96111
notes: release.body
97112
}
113+
break
98114
}
99115
}
116+
if (latest) break
100117
}
118+
119+
if (!latest) return
120+
121+
const rurl = `https://github.com/${account}/${repository}/releases/download/${
122+
latest.version
123+
}/RELEASES`
124+
const rres = await fetch(rurl)
125+
if (rres.status < 400) {
126+
const body = await rres.text()
127+
const matches = body.match(/[^ ]*\.nupkg/gim)
128+
const nuPKG = rurl.replace('RELEASES', matches[0])
129+
latest.RELEASES = body.replace(matches[0], nuPKG)
130+
}
131+
132+
return latest
133+
}
134+
}
135+
136+
class MemoryCache {
137+
constructor () {
138+
this.data = new Map()
139+
}
140+
async get (key) {
141+
return this.data.get(key)
142+
}
143+
async set (key, value) {
144+
this.data.set(key, value)
101145
}
102146
}
103147

test/index.js

Lines changed: 65 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const Updates = require('..')
66

77
const { TOKEN: token } = process.env
88

9+
const cache = 100
10+
911
const createServer = () =>
1012
new Promise(resolve => {
1113
const updates = new Updates({ token })
@@ -27,12 +29,14 @@ test('Updates', async t => {
2729
t.equal(res.status, 404)
2830

2931
await t.test('exists and has update', async t => {
30-
const res = await fetch(`${address}/dat-land/dat-desktop/darwin/1.0.0`)
31-
t.equal(res.status, 200)
32-
const body = await res.json()
33-
t.ok(body.name)
34-
t.match(body.url, /-mac\.zip$/)
35-
t.ok(body.notes)
32+
for (let i = 0; i < cache; i++) {
33+
const res = await fetch(`${address}/dat-land/dat-desktop/darwin/1.0.0`)
34+
t.equal(res.status, 200)
35+
const body = await res.json()
36+
t.ok(body.name)
37+
t.match(body.url, /-mac\.zip$/)
38+
t.ok(body.notes)
39+
}
3640
})
3741

3842
await t.test('exists but no updates', async t => {
@@ -44,60 +48,77 @@ test('Updates', async t => {
4448
const release = releases.find(
4549
release => !release.draft && !release.prerelease
4650
)
47-
res = await fetch(
48-
`${address}/dat-land/dat-desktop/darwin/${release.name}`
49-
)
50-
t.equal(res.status, 204)
51+
52+
for (let i = 0; i < cache; i++) {
53+
res = await fetch(
54+
`${address}/dat-land/dat-desktop/darwin/${release.name}`
55+
)
56+
t.equal(res.status, 204)
57+
}
5158
})
5259

5360
await t.test('exists but has no releases', async t => {
54-
const res = await fetch(
55-
`${address}/juliangruber/brace-expansion/darwin/0.0.0`
56-
)
57-
t.equal(res.status, 404)
61+
for (let i = 0; i < cache; i++) {
62+
const res = await fetch(
63+
`${address}/juliangruber/brace-expansion/darwin/0.0.0`
64+
)
65+
t.equal(res.status, 404)
66+
}
5867
})
5968

6069
await t.test("doesn't exist", async t => {
61-
const res = await fetch(`${address}/doesnot/exist-123123123/darwin/0.0.0`)
62-
t.equal(res.status, 404)
70+
for (let i = 0; i < cache; i++) {
71+
const res = await fetch(
72+
`${address}/doesnot/exist-123123123/darwin/0.0.0`
73+
)
74+
t.equal(res.status, 404)
75+
}
6376
})
6477
})
6578

6679
await t.test('Platforms', async t => {
6780
await t.test('Darwin', async t => {
68-
let res = await fetch(`${address}/dat-land/dat-desktop/darwin/1.0.0`)
69-
t.equal(res.status, 200)
70-
let body = await res.json()
71-
t.match(body.url, /-mac\.zip$/)
72-
73-
res = await fetch(`${address}/webtorrent/webtorrent-desktop/darwin/0.0.0`)
74-
t.equal(res.status, 200)
75-
body = await res.json()
76-
t.match(body.url, /-darwin\.zip$/)
81+
for (let i = 0; i < cache; i++) {
82+
let res = await fetch(`${address}/dat-land/dat-desktop/darwin/1.0.0`)
83+
t.equal(res.status, 200)
84+
let body = await res.json()
85+
t.match(body.url, /-mac\.zip$/)
86+
87+
res = await fetch(
88+
`${address}/webtorrent/webtorrent-desktop/darwin/0.0.0`
89+
)
90+
t.equal(res.status, 200)
91+
body = await res.json()
92+
t.match(body.url, /-darwin\.zip$/)
93+
}
7794
})
7895

7996
await t.test('Win32', async t => {
80-
const res = await fetch(`${address}/zeit/hyper/win32/0.0.0`)
81-
t.equal(res.status, 200)
82-
const body = await res.json()
83-
t.match(body.url, /\.exe$/)
84-
t.ok(body.name)
85-
86-
await t.test('RELEASES', async t => {
87-
let res = await fetch(
88-
`${address}/zeit/hyper/win32/0.0.0/RELEASES?some-extra`
89-
)
97+
for (let i = 0; i < cache; i++) {
98+
const res = await fetch(`${address}/zeit/hyper/win32/0.0.0`)
9099
t.equal(res.status, 200)
91-
const body = await res.text()
92-
t.match(
93-
body,
94-
/^[^ ]+ https:\/\/github.com\/zeit\/hyper\/releases\/download\/[^/]+\/hyper-[^-]+-full.nupkg [^ ]+$/
95-
)
100+
const body = await res.json()
101+
t.match(body.url, /\.exe$/)
102+
t.ok(body.name)
103+
}
96104

97-
res = await fetch(
98-
`${address}/juliangruber/brace-expansion/win32/0.0.0/RELEASES`
99-
)
100-
t.equal(res.status, 404)
105+
await t.test('RELEASES', async t => {
106+
for (let i = 0; i < cache; i++) {
107+
let res = await fetch(
108+
`${address}/zeit/hyper/win32/0.0.0/RELEASES?some-extra`
109+
)
110+
t.equal(res.status, 200)
111+
const body = await res.text()
112+
t.match(
113+
body,
114+
/^[^ ]+ https:\/\/github.com\/zeit\/hyper\/releases\/download\/[^/]+\/hyper-[^-]+-full.nupkg [^ ]+$/
115+
)
116+
117+
res = await fetch(
118+
`${address}/juliangruber/brace-expansion/win32/0.0.0/RELEASES`
119+
)
120+
t.equal(res.status, 404)
121+
}
101122
})
102123
})
103124
})

0 commit comments

Comments
 (0)