From f6c40d21cb099a9164bb380c607d5cc752fc3549 Mon Sep 17 00:00:00 2001 From: Srijan Tripathi <72181144+srijantrpth@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:44:00 +0000 Subject: [PATCH 1/7] Added Custom Headers Logic to Query --- src/Utils/request/query.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index 3431f625728..c198de5fea5 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -5,15 +5,26 @@ import { getResponseBody } from "@/Utils/request/request"; import { QueryOptions, Route } from "@/Utils/request/types"; import { makeHeaders, makeUrl } from "@/Utils/request/utils"; +// Extend the QueryOptions interface to include customHeaders +export interface ExtendedQueryOptions extends QueryOptions { + customHeaders?: Record; +} + async function queryRequest( { path, method, noAuth }: Route, - options?: QueryOptions, + options?: ExtendedQueryOptions, ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; + // Merge customHeaders with default headers + const headers = { + ...makeHeaders(noAuth ?? false), + ...(options?.customHeaders || {}), + }; + const fetchOptions: RequestInit = { method, - headers: makeHeaders(noAuth ?? false), + headers, signal: options?.signal, }; @@ -48,7 +59,7 @@ async function queryRequest( */ export default function query( route: Route, - options?: QueryOptions, + options?: ExtendedQueryOptions, ) { return ({ signal }: { signal: AbortSignal }) => { return queryRequest(route, { ...options, signal }); From 9ca80e04bb3e625864ce8420c006e3d20a620212 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Mon, 16 Dec 2024 18:52:59 +0530 Subject: [PATCH 2/7] suggestions + improve error objs. type safety --- src/Utils/request/query.ts | 17 +++++++---------- src/Utils/request/types.ts | 1 + src/Utils/request/utils.ts | 17 ++++------------- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index c198de5fea5..7ad9752a9e4 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -5,22 +5,19 @@ import { getResponseBody } from "@/Utils/request/request"; import { QueryOptions, Route } from "@/Utils/request/types"; import { makeHeaders, makeUrl } from "@/Utils/request/utils"; -// Extend the QueryOptions interface to include customHeaders -export interface ExtendedQueryOptions extends QueryOptions { - customHeaders?: Record; +declare module "@tanstack/react-query" { + interface Register { + defaultError: QueryError; + } } async function queryRequest( { path, method, noAuth }: Route, - options?: ExtendedQueryOptions, + options?: QueryOptions, ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; - // Merge customHeaders with default headers - const headers = { - ...makeHeaders(noAuth ?? false), - ...(options?.customHeaders || {}), - }; + const headers = makeHeaders(noAuth ?? false, options?.headers); const fetchOptions: RequestInit = { method, @@ -59,7 +56,7 @@ async function queryRequest( */ export default function query( route: Route, - options?: ExtendedQueryOptions, + options?: QueryOptions, ) { return ({ signal }: { signal: AbortSignal }) => { return queryRequest(route, { ...options, signal }); diff --git a/src/Utils/request/types.ts b/src/Utils/request/types.ts index 20f095ae6fd..2b4be31d28d 100644 --- a/src/Utils/request/types.ts +++ b/src/Utils/request/types.ts @@ -41,6 +41,7 @@ export interface QueryOptions { body?: TBody; silent?: boolean; signal?: AbortSignal; + headers?: HeadersInit; } export interface PaginatedResponse { diff --git a/src/Utils/request/utils.ts b/src/Utils/request/utils.ts index 8fd7bc96bea..6316becbe49 100644 --- a/src/Utils/request/utils.ts +++ b/src/Utils/request/utils.ts @@ -50,33 +50,24 @@ const ensurePathNotMissingReplacements = (path: string) => { } }; -export function makeHeaders(noAuth: boolean) { +export function makeHeaders(noAuth: boolean, additionalHeaders?: HeadersInit) { const headers = new Headers({ "Content-Type": "application/json", Accept: "application/json", + ...additionalHeaders, }); if (!noAuth) { - const token = getAuthorizationHeader(); + const token = localStorage.getItem(LocalStorageKeys.accessToken); if (token) { - headers.append("Authorization", token); + headers.append("Authorization", `Bearer ${token}`); } } return headers; } -export function getAuthorizationHeader() { - const bearerToken = localStorage.getItem(LocalStorageKeys.accessToken); - - if (bearerToken) { - return `Bearer ${bearerToken}`; - } - - return null; -} - export function mergeRequestOptions( options: RequestOptions, overrides: RequestOptions, From 0883fb4529d5e2da405e04f2c3fcc05d9e0f6bd7 Mon Sep 17 00:00:00 2001 From: Srijan Tripathi <72181144+srijantrpth@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:27:56 +0000 Subject: [PATCH 3/7] Improvised query headers handling and extend QueryOptions interface --- src/Utils/request/query.ts | 44 ++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index 7ad9752a9e4..acba624021a 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -5,19 +5,43 @@ import { getResponseBody } from "@/Utils/request/request"; import { QueryOptions, Route } from "@/Utils/request/types"; import { makeHeaders, makeUrl } from "@/Utils/request/utils"; -declare module "@tanstack/react-query" { - interface Register { - defaultError: QueryError; - } +/** + * Extend the QueryOptions interface to include customHeaders + * @template TBody - The type of the request body + */ +export interface QueryOptionsWithHeaders extends QueryOptions { + customHeaders?: Record; + headers?: HeadersInit; } +// Function to sanitize custom headers +const sanitizeHeaders = (headers: Record) => { + const sanitized: Record = {}; + for (const [key, value] of Object.entries(headers)) { + // Ensure header names follow RFC 7230 and values are safe + if ( + /^[!#$%&'*+-.^_`|~0-9a-zA-Z]+$/.test(key) && + typeof value === "string" && // Changed 'string' to "string" + !value.includes("\n") && // Changed '\n' to "\n" + !value.includes("\r") + ) { + // Changed '\r' to "\r" + sanitized[key] = value; + } + } + return sanitized; +}; + async function queryRequest( { path, method, noAuth }: Route, - options?: QueryOptions, + options?: QueryOptionsWithHeaders, ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; - const headers = makeHeaders(noAuth ?? false, options?.headers); + // Merge default headers with sanitized custom headers + const defaultHeaders = makeHeaders(noAuth ?? false); + const customHeaders = sanitizeHeaders(options?.customHeaders || {}); + const headers = { ...defaultHeaders, ...customHeaders }; // Merging headers manually const fetchOptions: RequestInit = { method, @@ -53,12 +77,14 @@ async function queryRequest( /** * Creates a TanStack Query compatible request function + * @template TData - The type of the response data + * @template TBody - The type of the request body */ export default function query( route: Route, - options?: QueryOptions, -) { - return ({ signal }: { signal: AbortSignal }) => { + options?: QueryOptionsWithHeaders, +): (params: { signal: AbortSignal }) => Promise { + return ({ signal }) => { return queryRequest(route, { ...options, signal }); }; } From 0f74197b470e586ee67228545e89b5e713e8f4cd Mon Sep 17 00:00:00 2001 From: Srijan Tripathi <72181144+srijantrpth@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:41:18 +0000 Subject: [PATCH 4/7] Fix: Add support for custom headers in queryRequest, allow header overwriting, and resolve type conflicts --- src/Utils/request/query.ts | 60 +++++++++++++++----------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index acba624021a..e9ce07f1e36 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -2,46 +2,36 @@ import careConfig from "@careConfig"; import { QueryError } from "@/Utils/request/queryError"; import { getResponseBody } from "@/Utils/request/request"; -import { QueryOptions, Route } from "@/Utils/request/types"; +import { Route } from "@/Utils/request/types"; +// Remove conflicting QueryOptions import import { makeHeaders, makeUrl } from "@/Utils/request/utils"; -/** - * Extend the QueryOptions interface to include customHeaders - * @template TBody - The type of the request body - */ -export interface QueryOptionsWithHeaders extends QueryOptions { - customHeaders?: Record; - headers?: HeadersInit; +// Define QueryOptions locally, including the 'headers' property +export interface QueryOptions { + body?: TBody; + queryParams?: Record; + pathParams?: Record; + silent?: boolean; + signal?: AbortSignal; + headers?: Record; // Add headers support } -// Function to sanitize custom headers -const sanitizeHeaders = (headers: Record) => { - const sanitized: Record = {}; - for (const [key, value] of Object.entries(headers)) { - // Ensure header names follow RFC 7230 and values are safe - if ( - /^[!#$%&'*+-.^_`|~0-9a-zA-Z]+$/.test(key) && - typeof value === "string" && // Changed 'string' to "string" - !value.includes("\n") && // Changed '\n' to "\n" - !value.includes("\r") - ) { - // Changed '\r' to "\r" - sanitized[key] = value; - } - } - return sanitized; -}; - async function queryRequest( { path, method, noAuth }: Route, - options?: QueryOptionsWithHeaders, + options?: QueryOptions, ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; - // Merge default headers with sanitized custom headers - const defaultHeaders = makeHeaders(noAuth ?? false); - const customHeaders = sanitizeHeaders(options?.customHeaders || {}); - const headers = { ...defaultHeaders, ...customHeaders }; // Merging headers manually + // Convert Headers object to a plain object + const defaultHeaders = Object.fromEntries( + makeHeaders(noAuth ?? false).entries(), + ); + + // Merge default headers with custom headers, with custom headers taking precedence + const headers = { + ...defaultHeaders, + ...(options?.headers || {}), + }; const fetchOptions: RequestInit = { method, @@ -77,14 +67,12 @@ async function queryRequest( /** * Creates a TanStack Query compatible request function - * @template TData - The type of the response data - * @template TBody - The type of the request body */ export default function query( route: Route, - options?: QueryOptionsWithHeaders, -): (params: { signal: AbortSignal }) => Promise { - return ({ signal }) => { + options?: QueryOptions, +) { + return ({ signal }: { signal: AbortSignal }) => { return queryRequest(route, { ...options, signal }); }; } From 596168e26154e1925297fc05a11b2cce69aefdae Mon Sep 17 00:00:00 2001 From: Srijan Tripathi <72181144+srijantrpth@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:17:53 +0000 Subject: [PATCH 5/7] Refactor: Headers API Implemented --- src/Utils/request/query.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index e9ce07f1e36..49f4abc4cb0 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -22,20 +22,21 @@ async function queryRequest( ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; - // Convert Headers object to a plain object - const defaultHeaders = Object.fromEntries( - makeHeaders(noAuth ?? false).entries(), - ); + // Create a Headers object from the default headers + const defaultHeaders = makeHeaders(noAuth ?? false); - // Merge default headers with custom headers, with custom headers taking precedence - const headers = { - ...defaultHeaders, - ...(options?.headers || {}), - }; + // If custom headers are provided, merge them with the default headers + if (options?.headers) { + const customHeaders = new Headers(options.headers); + customHeaders.forEach((value, key) => { + // Use set() to replace existing values, ensuring proper header handling + defaultHeaders.set(key, value); + }); + } const fetchOptions: RequestInit = { method, - headers, + headers: defaultHeaders, // Headers API object directly used signal: options?.signal, }; From 45a6a50f5e16346e20b4d7182b70a9596eb2795e Mon Sep 17 00:00:00 2001 From: Srijan Tripathi <72181144+srijantrpth@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:19:56 +0000 Subject: [PATCH 6/7] Fix: Headers API and Overwriting Headers --- src/Utils/request/query.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index 49f4abc4cb0..74ca5b22be8 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -25,12 +25,12 @@ async function queryRequest( // Create a Headers object from the default headers const defaultHeaders = makeHeaders(noAuth ?? false); - // If custom headers are provided, merge them with the default headers + // If custom headers are provided, append them to the default headers if (options?.headers) { const customHeaders = new Headers(options.headers); customHeaders.forEach((value, key) => { - // Use set() to replace existing values, ensuring proper header handling - defaultHeaders.set(key, value); + // Use append() to add new header values without overwriting existing ones + defaultHeaders.append(key, value); }); } From f897a9fe638d78823e8611323650c807e3a619c5 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Mon, 16 Dec 2024 20:56:46 +0530 Subject: [PATCH 7/7] cleanup implementation --- src/Utils/request/query.ts | 27 ++------------------------- src/Utils/request/utils.ts | 9 ++++----- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/Utils/request/query.ts b/src/Utils/request/query.ts index 74ca5b22be8..53fe96878a2 100644 --- a/src/Utils/request/query.ts +++ b/src/Utils/request/query.ts @@ -2,41 +2,18 @@ import careConfig from "@careConfig"; import { QueryError } from "@/Utils/request/queryError"; import { getResponseBody } from "@/Utils/request/request"; -import { Route } from "@/Utils/request/types"; -// Remove conflicting QueryOptions import +import { QueryOptions, Route } from "@/Utils/request/types"; import { makeHeaders, makeUrl } from "@/Utils/request/utils"; -// Define QueryOptions locally, including the 'headers' property -export interface QueryOptions { - body?: TBody; - queryParams?: Record; - pathParams?: Record; - silent?: boolean; - signal?: AbortSignal; - headers?: Record; // Add headers support -} - async function queryRequest( { path, method, noAuth }: Route, options?: QueryOptions, ): Promise { const url = `${careConfig.apiUrl}${makeUrl(path, options?.queryParams, options?.pathParams)}`; - // Create a Headers object from the default headers - const defaultHeaders = makeHeaders(noAuth ?? false); - - // If custom headers are provided, append them to the default headers - if (options?.headers) { - const customHeaders = new Headers(options.headers); - customHeaders.forEach((value, key) => { - // Use append() to add new header values without overwriting existing ones - defaultHeaders.append(key, value); - }); - } - const fetchOptions: RequestInit = { method, - headers: defaultHeaders, // Headers API object directly used + headers: makeHeaders(noAuth ?? false, options?.headers), signal: options?.signal, }; diff --git a/src/Utils/request/utils.ts b/src/Utils/request/utils.ts index 6316becbe49..03427c17b44 100644 --- a/src/Utils/request/utils.ts +++ b/src/Utils/request/utils.ts @@ -51,11 +51,10 @@ const ensurePathNotMissingReplacements = (path: string) => { }; export function makeHeaders(noAuth: boolean, additionalHeaders?: HeadersInit) { - const headers = new Headers({ - "Content-Type": "application/json", - Accept: "application/json", - ...additionalHeaders, - }); + const headers = new Headers(additionalHeaders); + + headers.set("Content-Type", "application/json"); + headers.append("Accept", "application/json"); if (!noAuth) { const token = localStorage.getItem(LocalStorageKeys.accessToken);