-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from takker99:stop-wrapping
feat: Stop convert `Promise<Response>` into Result object and simply add type annotations to `Response` instead
- Loading branch information
Showing
14 changed files
with
562 additions
and
300 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,17 @@ | ||
name: ci | ||
|
||
env: | ||
DENO_VERSION: 1.x | ||
|
||
on: [push, pull_request] | ||
|
||
env: | ||
DENO_VERSION: 2.x | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Install Deno | ||
uses: denoland/setup-deno@v1 | ||
- uses: denoland/setup-deno@v2 | ||
with: | ||
deno-version: ${{ env.DENO_VERSION }} | ||
- name: Check fmt | ||
run: deno task fmt --check | ||
- name: Run lint | ||
run: deno task lint | ||
- name: Run type check | ||
run: deno task check | ||
- name: Check all | ||
run: deno task check |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,25 @@ | ||
# cf. https://jsr.io/@core/unknownutil/3.18.1/.github/workflows/jsr.yml | ||
name: publish | ||
|
||
env: | ||
DENO_VERSION: 1.x | ||
DENO_VERSION: 2.x | ||
|
||
on: | ||
push: | ||
tags: | ||
- '*' | ||
- "*" | ||
|
||
permissions: | ||
contents: read | ||
id-token: write # The OIDC ID token is used for authentication with JSR. | ||
|
||
id-token: write | ||
|
||
jobs: | ||
publish: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Install Deno | ||
uses: denoland/setup-deno@v1 | ||
- uses: denoland/setup-deno@v2 | ||
with: | ||
deno-version: ${{ env.DENO_VERSION }} | ||
- name: Publish on tag | ||
run: deno task publish | ||
run: deno run --allow-env --allow-run=deno --allow-read --allow-write=deno.jsonc jsr:@david/publish-on-tag@0.1.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
name: Update | ||
|
||
on: | ||
schedule: | ||
- cron: "0 0 * * *" | ||
workflow_dispatch: | ||
|
||
env: | ||
DENO_VERSION: 2.x | ||
|
||
jobs: | ||
update: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: denoland/setup-deno@v2 | ||
with: | ||
deno-version: ${{ env.DENO_VERSION }} | ||
- name: Update dependencies | ||
run: | | ||
deno outdated --update &> ../output.txt | ||
env: | ||
NO_COLOR: 1 | ||
- name: Read ../output.txt | ||
id: log | ||
uses: juliangruber/read-file-action@v1 | ||
with: | ||
path: ../output.txt | ||
- uses: peter-evans/create-pull-request@v7 | ||
with: | ||
commit-message: "chore: Update Deno dependencies" | ||
title: "chore: Update Deno dependencies" | ||
body: | | ||
The output of `deno outdated --update` is | ||
``` | ||
${{ steps.log.outputs.content }} | ||
``` | ||
branch: update-deno-dependencies | ||
delete-branch: true | ||
sign-commits: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
docs/ | ||
coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,20 @@ | ||
{ | ||
"exclude": [ | ||
"docs/", | ||
"coverage/" | ||
], | ||
"exports": "./mod.ts", | ||
"imports": { | ||
"@std/http": "jsr:@std/http@^1.0.12", | ||
"@std/json": "jsr:@std/json@^1.0.1", | ||
"@std/testing": "jsr:@std/testing@^1.0.8" | ||
}, | ||
"name": "@takker/gyazo", | ||
"version": "0.0.0", | ||
"tasks": { | ||
// cf. https://github.com/jsr-core/unknownutil/blob/v3.18.1/deno.jsonc | ||
"check": "deno check --remote **/*.ts", | ||
"fmt": "deno fmt", | ||
"lint": "deno lint", | ||
"check:all": "deno task fmt && deno task lint && deno task check", | ||
"update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=jsr.io,registry.npmjs.org jsr:@molt/cli ./*.ts", | ||
"update:commit": "deno task -q update --commit --pre-commit=fmt,lint", | ||
"publish": "deno run --allow-env --allow-run=deno --allow-read --allow-write=deno.jsonc jsr:@david/publish-on-tag@0.1.x" | ||
"check": "deno fmt --check && deno lint && deno doc --lint mod.ts && deno test --doc --parallel --shuffle && deno publish --dry-run", | ||
"coverage": "deno test --allow-read=./ --parallel --shuffle --coverage && deno coverage --html", | ||
"doc": "deno doc --html mod.ts", | ||
"fix": "deno fmt && deno lint --fix && deno doc --lint mod.ts && deno test --doc --parallel --shuffle && deno publish --dry-run --allow-dirty" | ||
}, | ||
"imports": { "result": "npm:option-t@^49.1.0/plain_result" }, | ||
"exports": "./mod.ts" | ||
"version": "0.0.0" | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,140 +1,7 @@ | ||
import { isObject } from "./util.ts"; | ||
import { createErr, createOk, type Result } from "result"; | ||
|
||
export class UnexpectedResponseError extends Error { | ||
name = "UnexpectedResponseError"; | ||
status: number; | ||
statusText: string; | ||
body: string; | ||
path: URL; | ||
|
||
constructor( | ||
init: { status: number; statusText: string; body: string; path: URL }, | ||
) { | ||
super( | ||
`${init.status} ${init.statusText} when fetching ${init.path.toString()}`, | ||
); | ||
|
||
this.status = init.status; | ||
this.statusText = init.statusText; | ||
this.body = init.body; | ||
this.path = init.path; | ||
|
||
// @ts-ignore only available on V8 | ||
if (Error.captureStackTrace) { | ||
// @ts-ignore only available on V8 | ||
Error.captureStackTrace(this, UnexpectedResponseError); | ||
} | ||
} | ||
} | ||
|
||
export const isUnexpectedResponseError = ( | ||
e: unknown, | ||
): e is UnexpectedResponseError => | ||
(e instanceof Error) && e.name === "UnexpectedResponseError"; | ||
|
||
/** 400のときのerror object */ | ||
export interface BadRequestError { | ||
name: "BadRequestError"; | ||
message: string; | ||
} | ||
/** 401のときのerror object */ | ||
export interface UnauthorizedError { | ||
name: "UnauthorizedError"; | ||
message: string; | ||
} | ||
/** 403のときのerror object */ | ||
export interface NotPrivilegeError { | ||
name: "NotPrivilegeError"; | ||
message: string; | ||
} | ||
/** 404のときのerror object */ | ||
export interface NotFoundError { | ||
name: "NotFoundError"; | ||
message: string; | ||
} | ||
/** 422のときのerror object */ | ||
export interface InvalidParameterError { | ||
name: "InvalidParameterError"; | ||
message: string; | ||
} | ||
/** 429のときのerror object */ | ||
export interface RateLimitError { | ||
name: "RateLimitError"; | ||
/** | ||
* Gyazo API Error | ||
*/ | ||
export interface GyazoAPIError { | ||
/** error message */ | ||
message: string; | ||
} | ||
export type GyazoAPIError = | ||
| BadRequestError | ||
| UnauthorizedError | ||
| NotPrivilegeError | ||
| NotFoundError | ||
| InvalidParameterError | ||
| RateLimitError; | ||
|
||
/** responseが正常かどうかを確かめる | ||
* | ||
* @param response 確かめたいResponse | ||
* @return 変換できたらそのobjectを返す | ||
* @throws {UnexpectedResponseError} | ||
*/ | ||
export const checkResponse = async ( | ||
response: Response, | ||
): Promise< | ||
Result< | ||
string, | ||
GyazoAPIError | ||
> | ||
> => { | ||
const text = await response.text(); | ||
if (response.ok) return createOk(text); | ||
|
||
if (response.status === 400) { | ||
return createErr({ name: "BadRequestError", message: text }); | ||
} | ||
|
||
try { | ||
const json: unknown = JSON.parse(text); | ||
if (!isObject(json) || typeof json.message !== "string") { | ||
throw new UnexpectedResponseError({ | ||
status: response.status, | ||
statusText: response.statusText, | ||
body: text, | ||
path: new URL(response.url), | ||
}); | ||
} | ||
|
||
switch (response.status) { | ||
case 401: | ||
return createErr({ name: "UnauthorizedError", message: json.message }); | ||
case 403: | ||
return createErr({ name: "NotPrivilegeError", message: json.message }); | ||
case 404: | ||
return createErr({ name: "NotFoundError", message: json.message }); | ||
case 422: | ||
return createErr({ | ||
name: "InvalidParameterError", | ||
message: json.message, | ||
}); | ||
case 429: | ||
return createErr({ name: "RateLimitError", message: json.message }); | ||
default: | ||
throw new UnexpectedResponseError({ | ||
status: response.status, | ||
statusText: response.statusText, | ||
body: text, | ||
path: new URL(response.url), | ||
}); | ||
} | ||
} catch (e2: unknown) { | ||
if (e2 instanceof SyntaxError) { | ||
throw new UnexpectedResponseError({ | ||
status: response.status, | ||
statusText: response.statusText, | ||
body: text, | ||
path: new URL(response.url), | ||
}); | ||
} | ||
// JSONのparse error以外はそのまま投げる | ||
throw e2; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,55 @@ | ||
import { type OAuthOptions, setDefaults } from "./util.ts"; | ||
import { checkResponse, type GyazoAPIError } from "./error.ts"; | ||
import { createOk, isErr, type Result, unwrapOk } from "result"; | ||
import type { | ||
ClientErrorStatus, | ||
StatusCode, | ||
SuccessfulStatus, | ||
} from "@std/http/status"; | ||
import type { ResponseOfEndpoint } from "./targeted_response.ts"; | ||
import type { GyazoAPIError } from "./error.ts"; | ||
|
||
/** Gyazo account profile */ | ||
export interface Profile { | ||
/* email*/ | ||
/** email */ | ||
email: string; | ||
/* user name */ | ||
/** user name */ | ||
name: string; | ||
/* user id */ | ||
/** user id */ | ||
uid: string; | ||
/* user profile image */ | ||
/** user profile image */ | ||
profile_image: string; | ||
} | ||
|
||
/** get user profile | ||
/** get a Gyazo user profile | ||
* | ||
* @param init accessTokeなど | ||
* @see https://gyazo.com/api/docs/user#user | ||
* | ||
* @param init accessToken etc. | ||
*/ | ||
export const getProfile = async ( | ||
init: OAuthOptions, | ||
): Promise<Result<Profile, GyazoAPIError>> => { | ||
export const getProfile = <R extends Response | undefined>( | ||
init: OAuthOptions<R>, | ||
): Promise< | ||
| ResponseOfEndpoint< | ||
& { 200: Profile } | ||
& Record<ClientErrorStatus, GyazoAPIError> | ||
& Record<Exclude<StatusCode, SuccessfulStatus | ClientErrorStatus>, string> | ||
> | ||
| (undefined extends R ? undefined : never) | ||
> => { | ||
const { accessToken, fetch } = setDefaults(init ?? {}); | ||
|
||
const path = `https://api.gyazo.com/api/users/me?access_token=${accessToken}`; | ||
const res = await fetch(path); | ||
|
||
const checked = await checkResponse(res); | ||
if (isErr(checked)) return checked; | ||
return createOk(JSON.parse(unwrapOk(checked)) as Profile); | ||
return fetch(path) as Promise< | ||
| ResponseOfEndpoint< | ||
& { | ||
200: Profile; | ||
} | ||
& Record<ClientErrorStatus, GyazoAPIError> | ||
& Record< | ||
Exclude<StatusCode, SuccessfulStatus | ClientErrorStatus>, | ||
string | ||
> | ||
> | ||
| (undefined extends R ? undefined : never) | ||
>; | ||
}; |
Oops, something went wrong.