Просмотр исходного кода

Merge pull request #32743 from overleaf/ar-new-v1-api

[web] new v1 api client

GitOrigin-RevId: 7ba0deef0d10526198a2a6ba997c2dcff7b7e5a5
Andrew Rumble 5 месяцев назад
Родитель
Сommit
54d953cfff

+ 9 - 0
services/web/app/src/Features/V1/V1Api.mjs

@@ -4,6 +4,7 @@ import settings from '@overleaf/settings'
 import Errors from '../Errors/Errors.js'
 import { promisifyMultiResult } from '@overleaf/promise-utils'
 import OError from '@overleaf/o-error'
+import Modules from '../../infrastructure/Modules.mjs'
 
 // TODO: check what happens when these settings aren't defined
 const DEFAULT_V1_PARAMS = {
@@ -16,6 +17,14 @@ const DEFAULT_V1_PARAMS = {
   timeout: settings.apis.v1.timeout,
 }
 
+export async function makeV1Request(options) {
+  const results = await Modules.promises.hooks.fire('makeV1Request', options)
+  if (!Array.isArray(results) || results.length === 0) {
+    throw new Error('No module provides a makeV1Request implementation')
+  }
+  return results[0]
+}
+
 const v1Request = request.defaults(DEFAULT_V1_PARAMS)
 
 function makeRequest(options, callback) {

+ 27 - 0
services/web/app/src/infrastructure/Modules.d.ts

@@ -0,0 +1,27 @@
+import type { RequestInit, Response } from 'node-fetch'
+
+export type V1RequestOptions = Omit<RequestInit, 'method'> & {
+  uri: string
+  qs?: Record<string, string | undefined>
+  expectJson?: boolean
+  expectedStatusCodes?: number[]
+  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
+}
+
+export interface KnownHookSignatures {
+  makeV1Request: (
+    options: V1RequestOptions
+  ) => Promise<{ body: unknown; response: Response }>
+}
+
+export type HookName = keyof KnownHookSignatures | string
+
+export type HookParameters<K extends HookName> =
+  K extends keyof KnownHookSignatures
+    ? Parameters<KnownHookSignatures[K]>
+    : any[]
+
+export type HookReturnType<K extends HookName> =
+  K extends keyof KnownHookSignatures
+    ? Awaited<ReturnType<KnownHookSignatures[K]>>
+    : any

+ 32 - 8
services/web/app/src/infrastructure/Modules.mjs

@@ -12,12 +12,16 @@ import Metrics from '@overleaf/metrics'
 /** @import { WebModule } from "../../../types/web-module" */
 /** @import { RequestHandler } from "express" */
 
+/**
+ * @import { HookName, HookParameters, HookReturnType } from './Modules'
+ */
+
 const MODULE_BASE_PATH = Path.join(import.meta.dirname, '/../../../modules')
 
 /** @type {WebModule[]} */
 const _modules = []
 let _modulesLoaded = false
-/** @type {Record<string, any>} */
+/** @type {Partial<Record<HookName, Function[]>>} */
 const _hooks = {}
 
 /** @type {Record<string, RequestHandler[]>} */
@@ -46,9 +50,20 @@ async function loadModulesImpl() {
     await import(settingsCheckModule)
   }
   for (const moduleName of Settings.moduleImportSequence || []) {
-    const module = await import(
-      Path.join(MODULE_BASE_PATH, moduleName, 'index.mjs')
+    const typescriptModule = Path.join(
+      MODULE_BASE_PATH,
+      moduleName,
+      'index.mts'
     )
+    let module
+    if (fs.existsSync(typescriptModule)) {
+      module = await import(typescriptModule)
+    } else {
+      module = await import(
+        Path.join(MODULE_BASE_PATH, moduleName, 'index.mjs')
+      )
+    }
+
     /** @type {WebModule & {name: string}} */
     const loadedModule = module.default || module
 
@@ -194,8 +209,9 @@ async function attachHooks() {
 }
 
 /**
- * @param {any} name
- * @param {any} method
+ * @template {HookName} K
+ * @param {K} name
+ * @param {(...args: HookParameters<K>) => HookReturnType<K>| Promise<HookReturnType<K>>} method
  */
 function attachHook(name, method) {
   if (_hooks[name] == null) {
@@ -221,8 +237,10 @@ async function attachMiddleware() {
 }
 
 /**
- * @param {any} name
- * @param {...any} args
+ * @template {HookName} K
+ * @param {K} name
+ * @param {HookParameters<K>} args
+ * @returns {Promise<HookReturnType<K>[]>}
  */
 async function fireHook(name, ...args) {
   // ensure that modules are loaded if we need to fire a hook
@@ -250,6 +268,10 @@ async function getMiddleware(name) {
   return _middleware[name] || []
 }
 
+/**
+ * @typedef {<K extends HookName>(name: K, ...args: [...HookParameters<K>, (err: any, results?: HookReturnType<K>[]) => void]) => void} CallbackFireHook
+ */
+
 export default {
   applyNonCsrfRouter,
   applyRouter,
@@ -261,7 +283,9 @@ export default {
   start,
   hooks: {
     attach: attachHook,
-    fire: callbackify(fireHook),
+    fire: /** @type {CallbackFireHook} */ (
+      /** @type {unknown} */ (callbackify(/** @type {any} */ (fireHook)))
+    ),
   },
   middleware: getMiddleware,
   promises: {