Skip to content

Commit

Permalink
Fix fetching (#1719)
Browse files Browse the repository at this point in the history
  • Loading branch information
drwpow authored Jun 21, 2024
1 parent 7c7d715 commit a8f2253
Show file tree
Hide file tree
Showing 23 changed files with 625 additions and 150 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-icons-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-fetch": patch
---

Remove nanoid from dependencies
5 changes: 5 additions & 0 deletions .changeset/little-snails-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-fetch": patch
---

Fix "failed to execute fetch on Window" error
5 changes: 5 additions & 0 deletions .changeset/twenty-nails-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-fetch": patch
---

Revert customFetch API back to `fetch(input: Request)`
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ jobs:
run_install: true
- run: pnpm run build
- run: pnpm test
test-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: pnpm/action-setup@v4
with:
version: latest
run_install: true
- run: pnpm exec playwright install --with-deps
- run: pnpm run test-e2e
test-macos:
runs-on: macos-latest
steps:
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@
"lint": "pnpm run -r --parallel --aggregate-output lint",
"format": "pnpm run -r --parallel --aggregate-output format",
"test": "pnpm run -r --parallel --aggregate-output test",
"test-e2e": "pnpm run -r --parallel --aggregate-output test-e2e",
"version": "pnpm run build && changeset version && pnpm i"
},
"devDependencies": {
"@biomejs/biome": "^1.8.1",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.5",
"@playwright/test": "^1.44.1",
"@types/node": "^20.14.7",
"del-cli": "^5.1.0",
"prettier": "^3.3.2",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"vitest": "^1.6.0"
}
}
1 change: 0 additions & 1 deletion packages/openapi-fetch/examples/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"react-dom": "18.3.1"
},
"devDependencies": {
"@types/node": "20.14.6",
"@types/react": "18.3.1",
"@types/react-dom": "18.3.0",
"openapi-typescript": "workspace:^",
Expand Down
1 change: 0 additions & 1 deletion packages/openapi-fetch/examples/vue-3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.14.6",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/tsconfig": "^0.5.1",
"openapi-typescript": "workspace:^",
Expand Down
8 changes: 5 additions & 3 deletions packages/openapi-fetch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,17 @@
"build:cjs": "esbuild --bundle src/index.js --format=cjs --outfile=dist/cjs/index.cjs && cp dist/index.d.ts dist/cjs/index.d.cts",
"format": "biome format . --write",
"lint": "biome check .",
"generate-types": "openapi-typescript test/fixtures/api.yaml -o test/fixtures/api.d.ts",
"generate-types": "openapi-typescript -c test/redocly.yaml",
"pretest": "pnpm run generate-types",
"test": "pnpm run \"/^test:/\"",
"test:js": "vitest run",
"test:ts": "tsc --noEmit",
"test-e2e": "playwright test",
"e2e-vite-build": "vite build test/fixtures/e2e",
"e2e-vite-start": "vite preview test/fixtures/e2e",
"version": "pnpm run prepare && pnpm run build"
},
"dependencies": {
"nanoid": "^5.0.7",
"openapi-typescript-helpers": "workspace:^"
},
"devDependencies": {
Expand All @@ -76,6 +78,6 @@
"openapi-typescript-fetch": "^2.0.0",
"superagent": "^9.0.2",
"typescript": "^5.4.5",
"vitest": "^1.6.0"
"vite": "^5.3.1"
}
}
28 changes: 28 additions & 0 deletions packages/openapi-fetch/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineConfig, devices } from "@playwright/test";

const PORT = Number.parseInt(process.env.PORT || 4173 || "", 10);

export default defineConfig({
testMatch: "test/**/*.e2e.ts",
webServer: {
command: "pnpm run e2e-vite-build && pnpm run e2e-vite-start",
port: PORT,
},
use: {
baseURL: `http://localhost:${PORT}`,
},
projects: [
{
name: "chrome",
use: { ...devices["Desktop Chrome"] },
},
{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},
{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
],
});
2 changes: 1 addition & 1 deletion packages/openapi-fetch/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface ClientOptions extends Omit<RequestInit, "headers"> {
/** set the common root URL for all API requests */
baseUrl?: string;
/** custom fetch (defaults to globalThis.fetch) */
fetch?: (input: string, init?: RequestInit) => Promise<Response>;
fetch?: (input: Request) => Promise<Response>;
/** global querySerializer */
querySerializer?: QuerySerializer<unknown> | QuerySerializerOptions;
/** global bodySerializer */
Expand Down
93 changes: 53 additions & 40 deletions packages/openapi-fetch/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { nanoid } from "nanoid";

// settings & const
const DEFAULT_HEADERS = {
"Content-Type": "application/json",
Expand All @@ -21,6 +19,14 @@ class CustomRequest extends Request {
}
}

/**
* Returns a cheap, non-cryptographically-secure random ID
* Courtesy of @imranbarbhuiya (https://github.com/imranbarbhuiya)
*/
export function randomID() {
return Math.random().toString(36).slice(2, 11);
}

/**
* Create an openapi-fetch client.
* @type {import("./index.js").default}
Expand Down Expand Up @@ -84,56 +90,63 @@ export default function createClient(clientOptions) {
requestInit.headers.delete("Content-Type");
}

const id = nanoid();
let id;
let options;
let request = new CustomRequest(createFinalURL(schemaPath, { baseUrl, params, querySerializer }), requestInit);

// middleware (request)
const options = Object.freeze({
baseUrl,
fetch,
parseAs,
querySerializer,
bodySerializer,
});
for (const m of middlewares) {
if (m && typeof m === "object" && typeof m.onRequest === "function") {
const result = await m.onRequest({
request,
schemaPath,
params,
options,
id,
});
if (result) {
if (!(result instanceof Request)) {
throw new Error("onRequest: must return new Request() when modifying the request");
if (middlewares.length) {
id = randomID();

// middleware (request)
options = Object.freeze({
baseUrl,
fetch,
parseAs,
querySerializer,
bodySerializer,
});
for (const m of middlewares) {
if (m && typeof m === "object" && typeof m.onRequest === "function") {
const result = await m.onRequest({
request,
schemaPath,
params,
options,
id,
});
if (result) {
if (!(result instanceof Request)) {
throw new Error("onRequest: must return new Request() when modifying the request");
}
request = result;
}
request = result;
}
}
}

// fetch!
let response = await fetch(request.url, request);
let response = await fetch(request);

// middleware (response)
// execute in reverse-array order (first priority gets last transform)
for (let i = middlewares.length - 1; i >= 0; i--) {
const m = middlewares[i];
if (m && typeof m === "object" && typeof m.onResponse === "function") {
const result = await m.onResponse({
request,
response,
schemaPath,
params,
options,
id,
});
if (result) {
if (!(result instanceof Response)) {
throw new Error("onResponse: must return new Response() when modifying the response");
if (middlewares.length) {
for (let i = middlewares.length - 1; i >= 0; i--) {
const m = middlewares[i];
if (m && typeof m === "object" && typeof m.onResponse === "function") {
const result = await m.onResponse({
request,
response,
schemaPath,
params,
options,
id,
});
if (result) {
if (!(result instanceof Response)) {
throw new Error("onResponse: must return new Response() when modifying the response");
}
response = result;
}
response = result;
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/openapi-fetch/test-results/.last-run.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"status": "passed",
"failedTests": []
}
52 changes: 52 additions & 0 deletions packages/openapi-fetch/test/fixtures/e2e/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import createClient from "../../../src";
import type { paths } from "./e2e.d.ts";

const client = createClient<paths>({
baseUrl: "/api/v1",
});

/**
* Test 1: GET /api/v1/get
*/
async function testGet() {
const { data } = await client.GET("/get");
if (!data) {
throw new Error("/get: No data");
}
}

/**
* Test 2: POST /api/v1/post
*/
async function testPost() {
const { data } = await client.POST("/post", { body: { message: "POST" } });
if (!data) {
throw new Error("/post: No data");
}
}

/**
* Test 3: PUT /api/v1/multi-form
*/
async function testMultiForm() {
const { data } = await client.POST("/multi-form", {
body: {
message: "Form",
file: new File(["Hello, World!"], "hello.txt") as unknown as string,
},
});
if (!data) {
throw new Error("/multi-form: No data");
}
}

// run all tests immediately on load
(async () => {
await Promise.all([testGet(), testPost(), testMultiForm()]);

// add element Playwright is waiting for
const div = document.createElement("div");
div.setAttribute("data-status", "success");
div.innerHTML = "Success";
document.body.appendChild(div);
})();
Loading

0 comments on commit a8f2253

Please sign in to comment.