Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(cloudflare-kv, cloudflare-r2): move getBindings to utils and add default BUCKET for r2 #292

Merged
merged 10 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/content/6.drivers/cloudflare-r2-binding.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@ import cloudflareR2BindingDriver from "unstorage/drivers/cloudflare-r2-binding";

// Using binding name to be picked from globalThis
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: "MY_BUCKET" }),
driver: cloudflareR2BindingDriver({ binding: "BUCKET" }),
});

// Directly setting binding
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: globalThis.MY_BUCKET }),
driver: cloudflareR2BindingDriver({ binding: globalThis.BUCKET }),
});

// Using from Durable Objects and Workers using Modules Syntax
const storage = createStorage({
driver: cloudflareR2BindingDriver({ binding: this.env.BUCKET }),
});
```

**Options:**

- `binding`: Bucket binding or name.
- `binding`: Bucket binding or name. Default is `BUCKET`.
- `base`: Prefix all keys with base.
45 changes: 9 additions & 36 deletions src/drivers/cloudflare-kv-binding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="@cloudflare/workers-types" />
import { createError, defineDriver, joinKeys } from "./utils";
import { defineDriver, joinKeys } from "./utils";
import { getKVBinding } from "./utils/cloudflare";
export interface KVOptions {
binding?: string | KVNamespace;

Expand All @@ -11,12 +12,12 @@ export interface KVOptions {

const DRIVER_NAME = "cloudflare-kv-binding";

export default defineDriver((opts: KVOptions = {}) => {
export default defineDriver((opts: KVOptions) => {
const r = (key: string = "") => (opts.base ? joinKeys(opts.base, key) : key);

async function getKeys(base: string = "") {
base = r(base);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
const kvList = await binding.list(base ? { prefix: base } : undefined);
return kvList.keys.map((key) => key.name);
}
Expand All @@ -26,22 +27,22 @@ export default defineDriver((opts: KVOptions = {}) => {
options: opts,
async hasItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return (await binding.get(key)) !== null;
},
getItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.get(key);
},
setItem(key, value) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.put(key, value);
},
removeItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
return binding.delete(key);
},
getKeys() {
Expand All @@ -50,37 +51,9 @@ export default defineDriver((opts: KVOptions = {}) => {
);
},
async clear(base) {
const binding = getBinding(opts.binding);
const binding = getKVBinding(opts.binding);
const keys = await getKeys(base);
await Promise.all(keys.map((key) => binding.delete(key)));
},
};
});

function getBinding(binding: KVNamespace | string = "STORAGE") {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as KVNamespace;
}

if (!binding) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}
}

return binding;
}
53 changes: 13 additions & 40 deletions src/drivers/cloudflare-r2-binding.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
/// <reference types="@cloudflare/workers-types" />
import { createError, defineDriver, joinKeys } from "./utils";
import { defineDriver, joinKeys } from "./utils";
import { getR2Binding } from "./utils/cloudflare";

export interface CloudflareR2Options {
binding: string | R2Bucket;
binding?: string | R2Bucket;
base?: string;
}

// https://developers.cloudflare.com/r2/api/workers/workers-api-reference/

const DRIVER_NAME = "cloudflare-r2-binding";

export default defineDriver((opts: CloudflareR2Options) => {
export default defineDriver((opts: CloudflareR2Options = {}) => {
const r = (key: string = "") => (opts.base ? joinKeys(opts.base, key) : key);

const getKeys = async (base?: string) => {
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
const kvList = await binding.list(
base || opts.base ? { prefix: r(base) } : undefined
);
Expand All @@ -26,12 +27,12 @@
options: opts,
async hasItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return (await binding.head(key)) !== null;
},
async getMeta(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);

Check warning on line 35 in src/drivers/cloudflare-r2-binding.ts

View check run for this annotation

Codecov / codecov/patch

src/drivers/cloudflare-r2-binding.ts#L35

Added line #L35 was not covered by tests
const obj = await binding.head(key);
if (!obj) return null;
return {
Expand All @@ -42,27 +43,27 @@
},
getItem(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return binding.get(key, topts).then((r) => r?.text());
},
getItemRaw(key, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
return binding.get(key, topts).then((r) => r?.arrayBuffer());
},
async setItem(key, value, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.put(key, value, topts);
},
async setItemRaw(key, value, topts) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.put(key, value, topts);
},
async removeItem(key) {
key = r(key);
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
await binding.delete(key);
},
getKeys(base) {
Expand All @@ -71,37 +72,9 @@
);
},
async clear(base) {
const binding = getBinding(opts.binding);
const binding = getR2Binding(opts.binding);
const keys = await getKeys(base);
await binding.delete(keys);
},
};
});

function getBinding(binding: R2Bucket | string) {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as R2Bucket;
}

if (!binding) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
DRIVER_NAME,
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}
}

return binding;
}
38 changes: 38 additions & 0 deletions src/drivers/utils/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/// <reference types="@cloudflare/workers-types" />
import { createError } from "./index";

export function getBinding(binding: KVNamespace | R2Bucket | string) {
let bindingName = "[binding]";

if (typeof binding === "string") {
bindingName = binding;
binding = ((globalThis as any)[bindingName] ||
(globalThis as any).__env__?.[bindingName]) as KVNamespace | R2Bucket;
}

Check warning on line 11 in src/drivers/utils/cloudflare.ts

View check run for this annotation

Codecov / codecov/patch

src/drivers/utils/cloudflare.ts#L8-L11

Added lines #L8 - L11 were not covered by tests

if (!binding) {
throw createError(
"cloudflare",
`Invalid binding \`${bindingName}\`: \`${binding}\``
);
}

Check warning on line 18 in src/drivers/utils/cloudflare.ts

View check run for this annotation

Codecov / codecov/patch

src/drivers/utils/cloudflare.ts#L14-L18

Added lines #L14 - L18 were not covered by tests

for (const key of ["get", "put", "delete"]) {
if (!(key in binding)) {
throw createError(
"cloudflare",
`Invalid binding \`${bindingName}\`: \`${key}\` key is missing`
);
}

Check warning on line 26 in src/drivers/utils/cloudflare.ts

View check run for this annotation

Codecov / codecov/patch

src/drivers/utils/cloudflare.ts#L22-L26

Added lines #L22 - L26 were not covered by tests
}

return binding;
}

export function getKVBinding(binding: KVNamespace | string = "STORAGE") {
return getBinding(binding) as KVNamespace;
}

export function getR2Binding(binding: R2Bucket | string = "BUCKET") {
return getBinding(binding) as R2Bucket;
}