|
1 | | -import { snakeCase } from "change-case"; |
2 | | - |
3 | 1 | import { |
4 | 2 | type Action, |
5 | 3 | type ActionDispatcher, |
6 | | - type Encoding, |
| 4 | + type ActionDispatcherHook, |
7 | 5 | type Http, |
8 | | - type HttpMetaParams, |
9 | 6 | } from "../../interfaces"; |
10 | | -import { type mastodon } from "../../mastodon"; |
11 | | -import { sleep } from "../../utils"; |
12 | | -import { MastoHttpError, MastoTimeoutError } from "../errors"; |
13 | 7 | import { PaginatorHttp } from "./paginator-http"; |
14 | 8 |
|
15 | | -type PrimitiveAction = "fetch" | "create" | "update" | "remove" | "list"; |
16 | | - |
17 | | -export interface HttpActionDispatcherParams { |
18 | | - readonly mediaTimeout?: number; |
19 | | -} |
| 9 | +export type HttpActionType = "fetch" | "create" | "update" | "remove" | "list"; |
| 10 | +export type HttpAction = Action<HttpActionType>; |
20 | 11 |
|
21 | | -export class HttpActionDispatcher implements ActionDispatcher { |
| 12 | +export class HttpActionDispatcher implements ActionDispatcher<HttpAction> { |
22 | 13 | constructor( |
23 | 14 | private readonly http: Http, |
24 | | - private readonly params: HttpActionDispatcherParams = {}, |
| 15 | + private readonly hook: ActionDispatcherHook<HttpAction>, |
25 | 16 | ) {} |
26 | 17 |
|
27 | | - dispatch<T>(action: Action): T | Promise<T> { |
28 | | - const actionType = this.toPrimitiveAction(action.type); |
29 | | - const path = this.isPrimitiveAction(action.type) |
30 | | - ? action.path |
31 | | - : action.path + "/" + snakeCase(action.type); |
32 | | - const encoding = this.inferEncoding(actionType, path); |
33 | | - const meta: HttpMetaParams<Encoding> = { ...action.meta, encoding }; |
| 18 | + dispatch<T>(action: HttpAction): T | Promise<T> { |
| 19 | + if (this.hook != undefined) { |
| 20 | + action = this.hook.beforeDispatch(action); |
| 21 | + } |
| 22 | + |
| 23 | + let result: T | Promise<T>; |
34 | 24 |
|
35 | | - switch (actionType) { |
| 25 | + switch (action.type) { |
36 | 26 | case "fetch": { |
37 | | - return this.http.get(path, action.data, meta); |
| 27 | + result = this.http.get(action.path, action.data, action.meta); |
| 28 | + break; |
38 | 29 | } |
39 | 30 | case "create": { |
40 | | - if (path === "/api/v2/media") { |
41 | | - return this.http |
42 | | - .post<mastodon.v1.MediaAttachment>(path, action.data, meta) |
43 | | - .then((media) => { |
44 | | - return this.waitForMediaAttachment(media.id) as T; |
45 | | - }); |
46 | | - } |
47 | | - return this.http.post(path, action.data, meta); |
| 31 | + result = this.http.post(action.path, action.data, action.meta); |
| 32 | + break; |
48 | 33 | } |
49 | 34 | case "update": { |
50 | | - return this.http.patch(path, action.data, meta); |
| 35 | + result = this.http.patch(action.path, action.data, action.meta); |
| 36 | + break; |
51 | 37 | } |
52 | 38 | case "remove": { |
53 | | - return this.http.delete(path, action.data, meta); |
54 | | - } |
55 | | - case "list": { |
56 | | - return new PaginatorHttp(this.http, path, action.data) as T; |
| 39 | + result = this.http.delete(action.path, action.data, action.meta); |
| 40 | + break; |
57 | 41 | } |
58 | | - } |
59 | | - } |
60 | | - |
61 | | - private isPrimitiveAction(action: string): action is PrimitiveAction { |
62 | | - switch (action) { |
63 | | - case "fetch": |
64 | | - case "create": |
65 | | - case "update": |
66 | | - case "remove": |
67 | 42 | case "list": { |
68 | | - return true; |
69 | | - } |
70 | | - default: { |
71 | | - return false; |
72 | | - } |
73 | | - } |
74 | | - } |
75 | | - |
76 | | - private toPrimitiveAction(action: string): PrimitiveAction { |
77 | | - if (this.isPrimitiveAction(action)) { |
78 | | - return action; |
79 | | - } |
80 | | - |
81 | | - switch (action) { |
82 | | - case "lookup": |
83 | | - case "verify_credentials": { |
84 | | - return "fetch"; |
85 | | - } |
86 | | - case "update_credentials": { |
87 | | - return "update"; |
88 | | - } |
89 | | - default: { |
90 | | - return "create"; |
| 43 | + result = new PaginatorHttp(this.http, action.path, action.data) as T; |
| 44 | + break; |
91 | 45 | } |
92 | 46 | } |
93 | | - } |
94 | 47 |
|
95 | | - private inferEncoding(action: PrimitiveAction, path: string): Encoding { |
96 | | - if ( |
97 | | - (action === "create" && path === "/api/v1/accounts") || |
98 | | - (action === "update" && path === "/api/v1/accounts/update_credentials") || |
99 | | - (action === "create" && path === "/api/v1/email") || |
100 | | - (action === "create" && path === "/api/v1/featured_tag") || |
101 | | - (action === "create" && path === "/api/v1/media") || |
102 | | - (action === "create" && path === "/api/v2/media") |
103 | | - ) { |
104 | | - return "multipart-form"; |
| 48 | + /* eslint-disable unicorn/prefer-ternary, prettier/prettier */ |
| 49 | + if (result instanceof Promise) { |
| 50 | + return result.then((result) => this.hook?.afterDispatch(action, result)) as Promise<T>; |
| 51 | + } else { |
| 52 | + return this.hook.afterDispatch(action, result) as T; |
105 | 53 | } |
106 | | - |
107 | | - return "json"; |
108 | | - } |
109 | | - |
110 | | - private get mediaTimeout(): number { |
111 | | - return this.params.mediaTimeout ?? 60 * 1000; |
| 54 | + /* eslint-enable unicorn/prefer-ternary, prettier/prettier */ |
112 | 55 | } |
113 | | - |
114 | | - private waitForMediaAttachment = async ( |
115 | | - id: string, |
116 | | - ): Promise<mastodon.v1.MediaAttachment> => { |
117 | | - let media: mastodon.v1.MediaAttachment | undefined; |
118 | | - const signal = AbortSignal.timeout(this.mediaTimeout); |
119 | | - |
120 | | - while (media == undefined) { |
121 | | - if (signal.aborted) { |
122 | | - throw new MastoTimeoutError( |
123 | | - `Media processing timed out of ${this.mediaTimeout}ms`, |
124 | | - ); |
125 | | - } |
126 | | - |
127 | | - try { |
128 | | - await sleep(1000); |
129 | | - |
130 | | - const processing = await this.http.get<mastodon.v1.MediaAttachment>( |
131 | | - `/api/v1/media/${id}`, |
132 | | - ); |
133 | | - |
134 | | - if (processing.url != undefined) { |
135 | | - media = processing; |
136 | | - } |
137 | | - } catch (error) { |
138 | | - if (error instanceof MastoHttpError && error.statusCode === 404) { |
139 | | - continue; |
140 | | - } |
141 | | - throw error; |
142 | | - } |
143 | | - } |
144 | | - |
145 | | - return media; |
146 | | - }; |
147 | 56 | } |
0 commit comments