mirror of
https://github.com/bknd-io/bknd/
synced 2026-08-02 08:06:00 +00:00
modify MediaApi to support custom fetch implementation, defaults to native fetch (#158)
* modify MediaApi to support custom fetch implementation, defaults to native fetch added an optional `fetcher` parameter to allow usage of a custom fetch function in both `upload` and `fetcher` methods. Defaults to the standard `fetch` if none is provided. * fix tests and improve api fetcher types
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
/// <reference types="@types/bun" />
|
/// <reference types="@types/bun" />
|
||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { getFileFromContext, isFile, isReadableStream } from "../../src/core/utils";
|
import { getFileFromContext, isFile, isReadableStream } from "core/utils";
|
||||||
import { MediaApi } from "../../src/media/api/MediaApi";
|
import { MediaApi } from "media/api/MediaApi";
|
||||||
import { assetsPath, assetsTmpPath } from "../helper";
|
import { assetsPath, assetsTmpPath } from "../helper";
|
||||||
|
|
||||||
const mockedBackend = new Hono()
|
const mockedBackend = new Hono()
|
||||||
@@ -121,8 +121,12 @@ describe("MediaApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should upload file in various ways", async () => {
|
it("should upload file in various ways", async () => {
|
||||||
// @ts-ignore tests
|
const api = new MediaApi(
|
||||||
const api = new MediaApi({}, mockedBackend.request);
|
{
|
||||||
|
upload_fetcher: mockedBackend.request,
|
||||||
|
},
|
||||||
|
mockedBackend.request,
|
||||||
|
);
|
||||||
const file = Bun.file(`${assetsPath}/image.png`);
|
const file = Bun.file(`${assetsPath}/image.png`);
|
||||||
|
|
||||||
async function matches(req: Promise<any>, filename: string) {
|
async function matches(req: Promise<any>, filename: string) {
|
||||||
|
|||||||
+6
-1
@@ -8,6 +8,11 @@ import { omitKeys } from "core/utils";
|
|||||||
|
|
||||||
export type TApiUser = SafeUser;
|
export type TApiUser = SafeUser;
|
||||||
|
|
||||||
|
export type ApiFetcher = (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Response | Promise<Response>;
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
__BKND__: {
|
__BKND__: {
|
||||||
@@ -21,7 +26,7 @@ export type ApiOptions = {
|
|||||||
headers?: Headers;
|
headers?: Headers;
|
||||||
key?: string;
|
key?: string;
|
||||||
localStorage?: boolean;
|
localStorage?: boolean;
|
||||||
fetcher?: typeof fetch;
|
fetcher?: ApiFetcher;
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
verified?: boolean;
|
verified?: boolean;
|
||||||
} & (
|
} & (
|
||||||
|
|||||||
@@ -6,13 +6,17 @@ import {
|
|||||||
type TInput,
|
type TInput,
|
||||||
} from "modules/ModuleApi";
|
} from "modules/ModuleApi";
|
||||||
import type { FileWithPath } from "ui/elements/media/file-selector";
|
import type { FileWithPath } from "ui/elements/media/file-selector";
|
||||||
|
import type { ApiFetcher } from "Api";
|
||||||
|
|
||||||
export type MediaApiOptions = BaseModuleApiOptions & {};
|
export type MediaApiOptions = BaseModuleApiOptions & {
|
||||||
|
upload_fetcher: ApiFetcher;
|
||||||
|
};
|
||||||
|
|
||||||
export class MediaApi extends ModuleApi<MediaApiOptions> {
|
export class MediaApi extends ModuleApi<MediaApiOptions> {
|
||||||
protected override getDefaultOptions(): Partial<MediaApiOptions> {
|
protected override getDefaultOptions(): Partial<MediaApiOptions> {
|
||||||
return {
|
return {
|
||||||
basepath: "/api/media",
|
basepath: "/api/media",
|
||||||
|
upload_fetcher: fetch,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +113,12 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
filename?: string;
|
filename?: string;
|
||||||
_init?: Omit<RequestInit, "body">;
|
_init?: Omit<RequestInit, "body">;
|
||||||
path?: TInput;
|
path?: TInput;
|
||||||
|
fetcher?: ApiFetcher;
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (item instanceof Request || typeof item === "string") {
|
if (item instanceof Request || typeof item === "string") {
|
||||||
const res = await this.fetcher(item);
|
const fetcher = opts.fetcher ?? this.options.upload_fetcher;
|
||||||
|
const res = await fetcher(item);
|
||||||
if (!res.ok || !res.body) {
|
if (!res.ok || !res.body) {
|
||||||
throw new Error("Failed to fetch file");
|
throw new Error("Failed to fetch file");
|
||||||
}
|
}
|
||||||
@@ -143,6 +149,7 @@ export class MediaApi extends ModuleApi<MediaApiOptions> {
|
|||||||
item: Request | Response | string | File | ReadableStream,
|
item: Request | Response | string | File | ReadableStream,
|
||||||
opts?: {
|
opts?: {
|
||||||
_init?: Omit<RequestInit, "body">;
|
_init?: Omit<RequestInit, "body">;
|
||||||
|
fetcher?: typeof fetch;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return this.upload(item, {
|
return this.upload(item, {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { $console, type PrimaryFieldType } from "core";
|
import { $console, type PrimaryFieldType } from "core";
|
||||||
import { isDebug } from "core/env";
|
import { isDebug } from "core/env";
|
||||||
import { encodeSearch } from "core/utils/reqres";
|
import { encodeSearch } from "core/utils/reqres";
|
||||||
|
import type { ApiFetcher } from "Api";
|
||||||
|
|
||||||
export type { PrimaryFieldType };
|
export type { PrimaryFieldType };
|
||||||
export type BaseModuleApiOptions = {
|
export type BaseModuleApiOptions = {
|
||||||
@@ -24,11 +25,11 @@ export type ApiResponse<Data = any> = {
|
|||||||
export type TInput = string | (string | number | PrimaryFieldType)[];
|
export type TInput = string | (string | number | PrimaryFieldType)[];
|
||||||
|
|
||||||
export abstract class ModuleApi<Options extends BaseModuleApiOptions = BaseModuleApiOptions> {
|
export abstract class ModuleApi<Options extends BaseModuleApiOptions = BaseModuleApiOptions> {
|
||||||
protected fetcher: typeof fetch;
|
protected fetcher: ApiFetcher;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _options: Partial<Options> = {},
|
protected readonly _options: Partial<Options> = {},
|
||||||
fetcher?: typeof fetch,
|
fetcher?: ApiFetcher,
|
||||||
) {
|
) {
|
||||||
this.fetcher = fetcher ?? fetch;
|
this.fetcher = fetcher ?? fetch;
|
||||||
}
|
}
|
||||||
@@ -221,7 +222,7 @@ export class FetchPromise<T = ApiResponse<any>> implements Promise<T> {
|
|||||||
constructor(
|
constructor(
|
||||||
public request: Request,
|
public request: Request,
|
||||||
protected options?: {
|
protected options?: {
|
||||||
fetcher?: typeof fetch;
|
fetcher?: ApiFetcher;
|
||||||
verbose?: boolean;
|
verbose?: boolean;
|
||||||
},
|
},
|
||||||
// keep "any" here, it gets inferred correctly with the "refine" fn
|
// keep "any" here, it gets inferred correctly with the "refine" fn
|
||||||
|
|||||||
Reference in New Issue
Block a user