Browse Source

improve(plugin): support abortable controller of request api

charlie 3 years ago
parent
commit
e4b71a4ccf

+ 30 - 9
libs/src/modules/LSPlugin.Request.ts

@@ -3,6 +3,7 @@ import { EventEmitter } from 'eventemitter3'
 
 export type IRequestOptions<R = any> = {
   url: string
+  abortable: boolean
   headers: Record<string, string>
   method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
   data: Object | ArrayBuffer
@@ -40,8 +41,11 @@ export class LSPluginRequestTask<R = any> {
       this._client.once(
         genTaskCallbackType(this._requestId),
         (e) => {
-          // handle error
-          resolve(e)
+          if (e && e instanceof Error) {
+            reject(e)
+          } else {
+            resolve(e)
+          }
         }
       )
     })
@@ -61,9 +65,17 @@ export class LSPluginRequestTask<R = any> {
   }
 
   abort() {
-    if (this._aborted) return
+    if (
+      !this._requestOptions.abortable ||
+      this._aborted
+    ) return
+
+    this._client.ctx._execCallableAPI(
+      'http_request_abort',
+      this._requestId
+    )
 
-    // TODO: impl
+    this._aborted = true
   }
 
   get promise(): Promise<R> {
@@ -83,7 +95,7 @@ export class LSPluginRequestTask<R = any> {
  * A simple request client
  */
 export class LSPluginRequest extends EventEmitter {
-  constructor(private ctx: LSPluginUser) {
+  constructor(private _ctx: LSPluginUser) {
     super()
 
     // request callback listener
@@ -108,17 +120,26 @@ export class LSPluginRequest extends EventEmitter {
     )
   }
 
-  _request<R = any>(options: WithOptional<IRequestOptions<R>, keyof Omit<IRequestOptions, 'url'>>): LSPluginRequestTask<R> {
+  async _request<R extends {},
+    T extends WithOptional<IRequestOptions<R>, keyof Omit<IRequestOptions, 'url'>>>(options: T):
+    Promise<T extends Pick<IRequestOptions, 'abortable'> ? LSPluginRequestTask<R> : R> {
     const pid = this.ctx.baseInfo.id
     const { success, fail, final, ...requestOptions } = options
     const reqID = this.ctx.Experiments.invokeExperMethod('request', pid, requestOptions)
 
-    // TODO: impl
     const task = LSPluginRequest.createRequestTask(
       this.ctx.Request,
-      reqID, { success, fail, final }
+      reqID, options
     )
 
-    return task
+    if (!requestOptions.abortable) {
+      return task.promise
+    }
+
+    return task as any
+  }
+
+  get ctx(): LSPluginUser {
+    return this._ctx
   }
 }

+ 2 - 1
resources/package.json

@@ -37,7 +37,8 @@
     "@sentry/electron": "2.5.1",
     "posthog-js": "1.10.2",
     "@logseq/rsapi": "0.0.20",
-    "electron-deeplink": "1.0.10"
+    "electron-deeplink": "1.0.10",
+    "abort-controller": "3.0.0"
   },
   "devDependencies": {
     "@electron-forge/cli": "^6.0.0-beta.57",

+ 20 - 3
src/electron/electron/handler.cljs

@@ -8,6 +8,7 @@
             ["os" :as os]
             ["diff-match-patch" :as google-diff]
             ["/electron/utils" :as js-utils]
+            ["abort-controller" :as AbortController]
             [electron.fs-watcher :as watcher]
             [electron.configs :as cfgs]
             [promesa.core :as p]
@@ -368,8 +369,10 @@
 (defmethod handle :uninstallMarketPlugin [_ [_ id]]
   (plugin/uninstall! id))
 
+(def *request-abort-signals (atom {}))
+
 (defmethod handle :httpRequest [_ [_ _req-id opts]]
-  (let [{:keys [url method data returnType headers]} opts]
+  (let [{:keys [url abortable method data returnType headers]} opts]
     (when-let [[method type] (and (not (string/blank? url))
                                   [(keyword (string/upper-case (or method "GET")))
                                    (keyword (string/lower-case (or returnType "json")))])]
@@ -378,7 +381,11 @@
                             :headers (and headers (bean/->js headers))}
                            (merge (when (and (not (contains? #{:GET :HEAD} method)) data)
                                     ;; TODO: support type of arrayBuffer
-                                    {:body (js/JSON.stringify (bean/->js data))}))))
+                                    {:body (js/JSON.stringify (bean/->js data))})
+
+                                  (when-let [^js controller (and abortable (AbortController.))]
+                                    (swap! *request-abort-signals assoc _req-id controller)
+                                    {:signal (.-signal controller)}))))
           (p/then (fn [^js res]
                     (case type
                       :json
@@ -393,7 +400,17 @@
 
                       :text
                       (.text res))))
-          (p/catch identity)))))
+          (p/catch
+           (fn [^js e]
+             ;; TODO: handle special cases
+             (throw e)))
+          (p/finally
+           (fn []
+             (swap! *request-abort-signals dissoc _req-id)))))))
+
+(defmethod handle :httpRequestAbort [_ [_ _req-id]]
+  (when-let [^js controller (get @*request-abort-signals _req-id)]
+    (.abort controller)))
 
 (defmethod handle :quitAndInstall []
   (.quitAndInstall autoUpdater))

+ 2 - 2
src/main/logseq/api.cljs

@@ -820,9 +820,9 @@
           (p/catch #(req-cb %)))
       req-id)))
 
-(defn ^:export exper_abort_request
+(defn ^:export http_request_abort
   [req-id]
-  nil)
+  (ipc/ipc :httpRequestAbort req-id))
 
 ;; helpers
 (defn ^:export query_element_by_id