Pārlūkot izejas kodu

feat(apps/hermes/client): allow authorization headers (#1738)

* feat(apps/hermes/client): allow authorization headers

* bump version

* pass in headers to constructor instead

* address comments
Daniel Chew 1 gadu atpakaļ
vecāks
revīzija
07a7767ca5

+ 1 - 1
apps/hermes/client/js/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@pythnetwork/hermes-client",
-  "version": "1.0.3",
+  "version": "1.0.4",
   "description": "Pyth Hermes Client",
   "author": {
     "name": "Pyth Data Association"

+ 13 - 3
apps/hermes/client/js/src/HermesClient.ts

@@ -29,12 +29,17 @@ export type HermesClientConfig = {
    * it will timeout regardless of the retries at the configured `timeout` time.
    */
   httpRetries?: number;
+  /**
+   * Optional headers to be included in every request.
+   */
+  headers?: HeadersInit;
 };
 
 export class HermesClient {
   private baseURL: string;
   private timeout: DurationInMs;
   private httpRetries: number;
+  private headers: HeadersInit;
 
   /**
    * Constructs a new Connection.
@@ -46,6 +51,7 @@ export class HermesClient {
     this.baseURL = endpoint;
     this.timeout = config?.timeout ?? DEFAULT_TIMEOUT;
     this.httpRetries = config?.httpRetries ?? DEFAULT_HTTP_RETRIES;
+    this.headers = config?.headers ?? {};
   }
 
   private async httpRequest<ResponseData>(
@@ -58,7 +64,11 @@ export class HermesClient {
   ): Promise<ResponseData> {
     const controller = externalAbortController ?? new AbortController();
     const { signal } = controller;
-    options = { ...options, signal }; // Merge any existing options with the signal
+    options = {
+      ...options,
+      signal,
+      headers: { ...this.headers, ...options?.headers },
+    }; // Merge any existing options with the signal and headers
 
     // Set a timeout to abort the request if it takes too long
     const timeout = setTimeout(() => controller.abort(), this.timeout);
@@ -129,7 +139,7 @@ export class HermesClient {
       parsed?: boolean;
     }
   ): Promise<PriceUpdate> {
-    const url = new URL(`v2/updates/price/latest`, this.baseURL);
+    const url = new URL("v2/updates/price/latest", this.baseURL);
     for (const id of ids) {
       url.searchParams.append("ids[]", id);
     }
@@ -208,7 +218,7 @@ export class HermesClient {
       this.appendUrlSearchParams(url, transformedOptions);
     }
 
-    return new EventSource(url.toString());
+    return new EventSource(url.toString(), { headers: this.headers });
   }
 
   private appendUrlSearchParams(

+ 28 - 1
apps/hermes/client/js/src/examples/HermesClient.ts

@@ -28,8 +28,35 @@ const argv = yargs(hideBin(process.argv))
   })
   .parseSync();
 
+/**
+ * Extracts the endpoint and basic authorization headers from a given URL string.
+ *
+ * @param {string} urlString - The URL string containing the endpoint and optional basic auth credentials.
+ * @returns {{ endpoint: string; headers: HeadersInit }} An object containing the endpoint URL and headers.
+ */
+function extractBasicAuthorizationHeadersFromUrl(urlString: string): {
+  endpoint: string;
+  headers: HeadersInit;
+} {
+  const url = new URL(urlString);
+  const headers: HeadersInit = {};
+
+  if (url.username && url.password) {
+    headers["Authorization"] = `Basic ${btoa(
+      `${url.username}:${url.password}`
+    )}`;
+    url.username = "";
+    url.password = "";
+  }
+
+  return { endpoint: url.toString(), headers };
+}
+
 async function run() {
-  const connection = new HermesClient(argv.endpoint);
+  const { endpoint, headers } = extractBasicAuthorizationHeadersFromUrl(
+    argv.endpoint
+  );
+  const connection = new HermesClient(endpoint, { headers });
 
   const priceIds = argv.priceIds as string[];