-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
```bash curl -vv -H 'x-om-appversion: 2022.08.01-1-Web' http://localhost:59830/releases ``` ```json { "published_at": "2024-02-06T15:08:00Z", "code": 24020611, "news": { "en-US": "<TEXT>" }, "apk": { "name": "OrganicMaps-24020611-web-release.apk", "size": 62334329, "url": "https://github.com/organicmaps/organicmaps/releases/download/2024.02.06-11-android/OrganicMaps-24020611-web-release.apk" } } ``` Signed-off-by: Roman Tsisyk <[email protected]>
- Loading branch information
Showing
5 changed files
with
209 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { parseAppVersion, parseApkName } from './versions'; | ||
|
||
const GITHUB_RELEASES_URL: string = 'https://api.github.com/repos/organicmaps/organicmaps/releases'; | ||
// https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api?apiVersion=2022-11-28#authenticating-with-a-personal-access-token | ||
const GITHUB_BEARER_TOKEN: string = | ||
'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'; | ||
|
||
interface AppReleaseMetadata { | ||
published_at: Date; | ||
code: number; | ||
flavor?: string; | ||
type?: string; | ||
apk: { | ||
url: string; | ||
name: string; | ||
size: number; | ||
}; | ||
// TODO: figure out how to define map properly. | ||
news: { | ||
'en-US': string; | ||
}; | ||
} | ||
|
||
interface GitHubReleaseAssetMetadata { | ||
browser_download_url: string; | ||
name: string; | ||
size: number; | ||
content_type: string; | ||
state: string; | ||
} | ||
|
||
interface GitHubReleaseMetadata { | ||
published_at: Date; | ||
draft: boolean; | ||
prerelease: boolean; | ||
body: string; | ||
assets: [GitHubReleaseAssetMetadata]; | ||
} | ||
|
||
export async function getLatestRelease(request: Request) { | ||
const appVersion = parseAppVersion(request.headers.get('x-om-appversion')); | ||
if (!appVersion) return new Response('Unknown app version', { status: 400 }); | ||
|
||
// The release version doesn't have `-release` suffix, thus type should be `undefined`. | ||
if (appVersion.flavor != 'web' || appVersion.type !== undefined) | ||
return new Response('Unknown app version', { status: 400 }); | ||
|
||
const response = await fetch(GITHUB_RELEASES_URL, { | ||
cf: { | ||
// Always cache this fetch (including 404 responses) regardless of content type | ||
// for a max of 30 minutes before revalidating the resource | ||
cacheTtl: 30 * 60, | ||
cacheEverything: true, | ||
}, | ||
headers: { | ||
Accept: 'application/vnd.github+json', | ||
'User-Agent': 'curl/8.4.0', // GitHub returns 403 without this. | ||
'X-GitHub-Api-Version': '2022-11-28', | ||
Authorization: `Bearer ${GITHUB_BEARER_TOKEN}`, | ||
}, | ||
}); | ||
if (response.status != 200) | ||
return new Response(`Bad response status ${response.status} ${response.statusText} ${response.body} from GitHub`, { | ||
status: 500, | ||
}); | ||
|
||
const releases = (await response.json()) as [GitHubReleaseMetadata]; | ||
const release = releases.find((release) => release.draft == false && release.prerelease == false); | ||
if (release == undefined) return new Response('No published release in GitHub response', { status: 500 }); | ||
|
||
const apk = release.assets.find( | ||
(asset) => asset.content_type == 'application/vnd.android.package-archive' && asset.name.endsWith('.apk'), | ||
); | ||
if (!apk) throw new Error('The latest release does not have APK asset'); | ||
const apkVersion = parseApkName(apk.name); | ||
if (!apkVersion) throw new Error(`Failed to parse APK name: ${apk}`); | ||
if (apkVersion.flavor != 'web' || apkVersion.type != 'release') throw new Error(`Unsupported APK name: ${apk}`); | ||
|
||
const result: AppReleaseMetadata = { | ||
published_at: release.published_at, | ||
code: apkVersion.code, | ||
news: { | ||
'en-US': release.body, | ||
}, | ||
apk: { | ||
name: apk.name, | ||
size: apk.size, | ||
url: apk.browser_download_url, | ||
}, | ||
}; | ||
|
||
return new Response(JSON.stringify(result), { | ||
headers: { 'Content-Type': 'application/json' }, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { describe, expect, test } from '@jest/globals'; | ||
import { getLatestRelease } from '../src/releases'; | ||
|
||
describe('Get app release version for flavor', () => { | ||
const flavors = ['2022.08.23-1-web']; | ||
for (let flavor of flavors) { | ||
test(flavor, async () => { | ||
let req = new Request('http://127.0.0.1:8787/releases', { | ||
headers: { | ||
'X-OM-AppVersion': flavor.toLowerCase(), | ||
}, | ||
}); | ||
const response = await getLatestRelease(req); | ||
// TODO: How to print response.text in case of error? | ||
expect(response.status).toBe(200); | ||
const result = JSON.parse(await response.text()); | ||
expect(Number.parseInt(result.code)).toBeGreaterThanOrEqual(23040200); | ||
expect(result.apk).toBeDefined(); | ||
}); | ||
} | ||
}); | ||
|
||
describe('Unsupported flavors for app update checks', () => { | ||
const unsupported = [ | ||
'garbage', | ||
'', | ||
'20220823', | ||
'2022.08', | ||
'2022.08.23', // Older iOS clients | ||
'2022.08.23-1-Google-beta', | ||
'2022.08.23-5-Google-debug', | ||
'2022.08.23-1-fdroid-beta', | ||
'2022.08.23-1-fdroid-debug', | ||
'2022.08.23-1-web-beta', | ||
'2022.08.23-1-web-debug', | ||
'2022.08.23-1-Huawei-beta', | ||
'2022.08.23-1-Huawei-debug', | ||
// Mac OS version is not published yet anywhere. | ||
'2023.04.28-9-592bca9a-dirty-Darwin', | ||
'2023.04.28-9-592bca9a-Darwin', | ||
]; | ||
for (let flavor of unsupported) { | ||
test(flavor, async () => { | ||
let req = new Request('http://127.0.0.1:8787/releases', { | ||
headers: { | ||
'X-OM-AppVersion': flavor.toLowerCase(), | ||
}, | ||
}); | ||
try { | ||
const response = await getLatestRelease(req); | ||
expect(response.status).toBeGreaterThanOrEqual(400); | ||
} catch (err) { | ||
expect(err).toContain('Unsupported app version'); | ||
} | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters