Skip to content

Commit

Permalink
feat: createWebWorkerHandler for Cloudflare and Deno. Deprecates `c…
Browse files Browse the repository at this point in the history
…reateCloudflareHandler` (#287)
  • Loading branch information
baoshan authored Jul 1, 2022
1 parent 4a63390 commit 31b3f70
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 59 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -963,20 +963,20 @@ function onUnhandledRequest(request, response) {
</tbody>
</table>

### `createCloudflareHandler(app, options)`
### `createWebWorkerHandler(app, options)`

Event handler for Cloudflare workers.
Event handler for web worker environments (Cloudflare workers or Deno).

```js
// worker.js
import { OAuthApp, createCloudflareHandler } from "@octokit/oauth-app";
import { OAuthApp, createWebWorkerHandler } from "@octokit/oauth-app";
const app = new OAuthApp({
clientType: "oauth-app",
clientId: "1234567890abcdef1234",
clientSecret: "1234567890abcdef1234567890abcdef12345678",
});

const handleRequest = createCloudflareHandler(app, {
const handleRequest = createWebWorkerHandler(app, {
pathPrefix: "/api/github/oauth",
});

Expand Down
31 changes: 17 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,52 @@ import {
GetUserOctokitWithStateInterface,
} from "./methods/get-user-octokit";
import {
getWebFlowAuthorizationUrlWithState,
GetWebFlowAuthorizationUrlInterface,
getWebFlowAuthorizationUrlWithState,
} from "./methods/get-web-flow-authorization-url";
import {
createTokenWithState,
CreateTokenInterface,
createTokenWithState,
} from "./methods/create-token";
import {
checkTokenWithState,
CheckTokenInterface,
checkTokenWithState,
} from "./methods/check-token";
import {
resetTokenWithState,
ResetTokenInterface,
resetTokenWithState,
} from "./methods/reset-token";
import {
refreshTokenWithState,
RefreshTokenInterface,
refreshTokenWithState,
} from "./methods/refresh-token";
import {
scopeTokenWithState,
ScopeTokenInterface,
scopeTokenWithState,
} from "./methods/scope-token";
import {
deleteTokenWithState,
DeleteTokenInterface,
deleteTokenWithState,
} from "./methods/delete-token";
import {
deleteAuthorizationWithState,
DeleteAuthorizationInterface,
deleteAuthorizationWithState,
} from "./methods/delete-authorization";

import {
Options,
import type {
AddEventHandler,
ClientType,
ClientTypeFromOptions,
ConstructorOptions,
OctokitTypeFromOptions,
ClientTypeFromOptions,
ClientType,
AddEventHandler,
Options,
State,
} from "./types";
export { createNodeMiddleware } from "./middleware/node/index";
export { createCloudflareHandler } from "./middleware/cloudflare/index";
export {
createCloudflareHandler,
createWebWorkerHandler,
} from "./middleware/web-worker/index";
export { createAWSLambdaAPIGatewayV2Handler } from "./middleware/aws-lambda/api-gateway-v2";

type Constructor<T> = new (...args: any[]) => T;
Expand Down
4 changes: 2 additions & 2 deletions src/middleware/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ middleware
├── on-unhandled-request-default.ts
├── types.ts
├── node/
├── cloudflare/ (to be implemented)
├── web-worker/ (Cloudflare Workers & Deno)
└── deno/ (to be implemented)
```

## Generic HTTP Handler

[`handleRequest`](handle-request.ts) function is an abstract HTTP handler which accepts an `OctokitRequest` and returns an `OctokitResponse` if the request matches any predefined route.

> Different environments (e.g., Node.js, Cloudflare, etc.) exposes different APIs when processing HTTP requests (e.g., [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) for Node.js, [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) for Cloudflare workers, etc.). Two HTTP-related types ([`OctokitRequest` and `OctokitResponse`](./types.ts)) are generalized to make an abstract HTTP handler possible.
> Different environments (e.g., Node.js, Cloudflare Workers, Deno, etc.) exposes different APIs when processing HTTP requests (e.g., [`IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) for Node.js, [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) for Cloudflare workers, etc.). Two HTTP-related types ([`OctokitRequest` and `OctokitResponse`](./types.ts)) are generalized to make an abstract HTTP handler possible.
To share the behavior and capability with the existing Node.js middleware (and be compatible with [OAuth user authentication strategy in the browser](https://github.com/octokit/auth-oauth-user-client.js)), it is better to implement your HTTP handler/middleware based on `handleRequest` function.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ import { onUnhandledRequestDefault } from "../on-unhandled-request-default";
import { OAuthApp } from "../../index";
import { HandlerOptions } from "../types";

async function onUnhandledRequestDefaultCloudflare(
async function onUnhandledRequestDefaultWebWorker(
request: Request
): Promise<Response> {
const octokitRequest = parseRequest(request);
const octokitResponse = onUnhandledRequestDefault(octokitRequest);
return sendResponse(octokitResponse);
}

export function createCloudflareHandler<T>(
export function createWebWorkerHandler<T>(
app: OAuthApp<T>,
{
pathPrefix,
onUnhandledRequest = onUnhandledRequestDefaultCloudflare,
onUnhandledRequest = onUnhandledRequestDefaultWebWorker,
}: HandlerOptions & {
onUnhandledRequest?: (request: Request) => Response | Promise<Response>;
} = {}
Expand All @@ -34,3 +34,13 @@ export function createCloudflareHandler<T>(
: await onUnhandledRequest(request);
};
}

/** @deprecated */
export function createCloudflareHandler<T>(
...args: Parameters<typeof createWebWorkerHandler>
) {
args[0].octokit.log.warn(
"[@octokit/oauth-app] `createCloudflareHandler` is deprecated, use `createWebWorkerHandler` instead"
);
return createWebWorkerHandler(...args);
}
File renamed without changes.
File renamed without changes.
54 changes: 53 additions & 1 deletion test/deprecations.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
import { URL } from "url";
import * as nodeFetch from "node-fetch";
import fromEntries from "fromentries";
import { createCloudflareHandler, OAuthApp } from "../src";
import { Octokit } from "@octokit/core";

describe("deprecations", () => {
it.todo("no deprecations at this point");
beforeAll(() => {
Object.fromEntries ||= fromEntries;
(global as any).Request = nodeFetch.Request;
(global as any).Response = nodeFetch.Response;
});

afterAll(() => {
delete (global as any).Request;
delete (global as any).Response;
});

it("createCloudflareHandler works but logs out deprecation message", async () => {
const warn = jest.fn().mockResolvedValue(undefined);
const handleRequest = createCloudflareHandler(
new OAuthApp({
clientType: "github-app",
clientId: "client_id_123",
clientSecret: "client_secret_456",
Octokit: Octokit.defaults({
log: {
debug: () => undefined,
info: () => undefined,
warn,
error: () => undefined,
},
}),
})
);

expect(warn.mock.calls.length).toEqual(1);
expect(warn.mock.calls[0][0]).toEqual(
"[@octokit/oauth-app] `createCloudflareHandler` is deprecated, use `createWebWorkerHandler` instead"
);

const request = new Request("/api/github/oauth/login");
const { status, headers } = await handleRequest(request);

expect(status).toEqual(302);
const url = new URL(headers.get("location") as string);
expect(url).toMatchObject({
origin: "https://github.com",
pathname: "/login/oauth/authorize",
});
expect(url.searchParams.get("client_id")).toEqual("client_id_123");
expect(url.searchParams.get("state")).toMatch(/^\w+$/);
expect(url.searchParams.get("scope")).toEqual(null);
});
});
2 changes: 1 addition & 1 deletion test/node-middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ describe("createNodeMiddleware(app)", () => {
expect(response.status).toEqual(404);
});

it("GET /api/github/oauth/callback without code or state", async () => {
it("GET /api/github/oauth/callback without code", async () => {
const appMock = {};

const server = createServer(
Expand Down
Loading

0 comments on commit 31b3f70

Please sign in to comment.