Skip to content

Commit feb91ee

Browse files
committed
feat: add PackageApiR2Bucket
1 parent 122099f commit feb91ee

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

codebook.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ words = [
44
"bitbucket",
55
"braintree",
66
"cloudflare",
7+
"cloudflarestorage",
78
"cwd",
89
"dompurify",
910
"fflate",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
2+
import { sdkStreamMixin } from "@smithy/util-stream";
3+
import { fromAny } from "@total-typescript/shoehorn";
4+
import { mockClient } from "aws-sdk-client-mock";
5+
import { compressSync, strToU8 } from "fflate";
6+
import { goTry } from "go-go-try";
7+
import { beforeEach } from "node:test";
8+
import { Readable } from "stream";
9+
import { expect, test } from "vitest";
10+
import { PackageApiR2Bucket } from "./package-api-r2-bucket";
11+
12+
const s3Mock = mockClient(S3Client);
13+
14+
beforeEach(() => {
15+
s3Mock.reset();
16+
});
17+
18+
test("setPackageApi fail", async () => {
19+
s3Mock.on(PutObjectCommand).rejects("test");
20+
const db = new PackageApiR2Bucket();
21+
const [err] = await goTry(db.setPackageApi("foo", fromAny({ name: "foo" })));
22+
expect(err).toBeDefined();
23+
});
24+
25+
test("setPackageApi success", async () => {
26+
s3Mock.on(PutObjectCommand).resolves({});
27+
const db = new PackageApiR2Bucket();
28+
const [err] = await goTry(db.setPackageApi("foo", fromAny({ name: "foo" })));
29+
expect(err).toBeUndefined();
30+
});
31+
32+
test("getPackageApi fail", async () => {
33+
s3Mock.on(GetObjectCommand).rejects("test");
34+
const db = new PackageApiR2Bucket();
35+
const [err] = await goTry(db.getPackageApi("foo"));
36+
expect(err).toBeDefined();
37+
});
38+
39+
test("getPackageApi success", async () => {
40+
const pkgApiGz = compressSync(strToU8(JSON.stringify({ name: "foo" })));
41+
const stream = new Readable();
42+
stream.push(pkgApiGz);
43+
stream.push(null);
44+
s3Mock.on(GetObjectCommand).resolves({ Body: sdkStreamMixin(stream) });
45+
const db = new PackageApiR2Bucket();
46+
const [err, pkgApi] = await goTry(db.getPackageApi("foo"));
47+
expect(err).toBeUndefined();
48+
expect(pkgApi).toMatchInlineSnapshot(`
49+
{
50+
"name": "foo",
51+
}
52+
`);
53+
});

lib/db/package-api-r2-bucket.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
2+
import type { PackageApi } from "@jsdocs-io/extractor";
3+
import { compressSync, decompressSync, strFromU8, strToU8 } from "fflate";
4+
import { serverEnv } from "../server-env";
5+
import type { PackageApiDb } from "./package-api-db";
6+
7+
export class PackageApiR2Bucket implements PackageApiDb {
8+
dbName = "PackageApiR2Bucket";
9+
#s3 = new S3Client({
10+
region: "auto",
11+
endpoint: `https://${serverEnv.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`,
12+
credentials: {
13+
accessKeyId: serverEnv.CF_ACCESS_KEY_ID,
14+
secretAccessKey: serverEnv.CF_SECRET_ACCESS_KEY,
15+
},
16+
});
17+
18+
async getPackageApi(pkgId: string): Promise<PackageApi> {
19+
const cmd = new GetObjectCommand({
20+
Bucket: serverEnv.CF_BUCKET_NAME,
21+
Key: `${pkgId}.gz`,
22+
});
23+
const res = await this.#s3.send(cmd);
24+
const body = await res.Body!.transformToByteArray();
25+
const pkgApiJson = strFromU8(decompressSync(body));
26+
const pkgApi = JSON.parse(pkgApiJson) as unknown as PackageApi;
27+
return pkgApi;
28+
}
29+
30+
async setPackageApi(pkgId: string, pkgApi: PackageApi) {
31+
const pkgApiJson = JSON.stringify(pkgApi);
32+
const pkgApiGz = compressSync(strToU8(pkgApiJson));
33+
const cmd = new PutObjectCommand({
34+
Bucket: serverEnv.CF_BUCKET_NAME,
35+
Key: `${pkgId}.gz`,
36+
Body: pkgApiGz,
37+
});
38+
await this.#s3.send(cmd);
39+
}
40+
}

0 commit comments

Comments
 (0)