-
Notifications
You must be signed in to change notification settings - Fork 188
[jslib] Refactored Acknowledgements with Enhanced HTTP Utilities #9999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
HenriRabalais
wants to merge
20
commits into
aces:main
Choose a base branch
from
HenriRabalais:2025-09-08_addhttpclient
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
6ec9f13
added base level files before testing
ridz1208 0915473
added package lock
ridz1208 70f26fa
fixing fan
ridz1208 e3a7a1f
fix
kongtiaowang 57cec43
fix 2
kongtiaowang 98e382b
fix 3
kongtiaowang 927d0f4
fix-add data:acknowledgements
kongtiaowang 266fa49
cs fix
kongtiaowang dc13572
addressed Mario's review'
ridz1208 92ab272
fixing static tests
ridz1208 2171b89
addressing review
ridz1208 a1443e9
addressing review
ridz1208 f99f7e2
reverting to aces/main package.lock.json
ridz1208 6eacf09
attempted to resolve issue
HenriRabalais 1ce5848
trying to fix errors
HenriRabalais dd82006
reverting NDB Page
HenriRabalais 2cd11b9
fixing base url
HenriRabalais bcbf998
fixing error
HenriRabalais 66176e1
fixing error
HenriRabalais a01c9df
fix cs
kongtiaowang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import {HttpError} from './HttpError'; | ||
|
|
||
| /** | ||
| * Error thrown for network-level issues (e.g., no internet connection, DNS failure). | ||
| */ | ||
| export class ApiNetworkError extends HttpError { | ||
| /** | ||
| * @param request The Request object that generated the error. | ||
| * @param message The error message. | ||
| */ | ||
| constructor(request: Request, message?: string) { | ||
| super( | ||
| request, | ||
| undefined, | ||
| message || 'Network error occurred during API call.' | ||
| ); | ||
| this.name = 'APINetworkError'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import {HttpError} from './HttpError'; | ||
|
|
||
| /** | ||
| * Error thrown for non-2xx HTTP responses from the API. | ||
| * It includes the raw Response object for additional context. | ||
| */ | ||
| export class ApiResponseError extends HttpError { | ||
| /** | ||
| * | ||
| * @param request The Request object that generated the error. | ||
| * @param response The raw HTTP Response object. | ||
| * @param message The error message. | ||
| */ | ||
| constructor(request: Request, response: Response, message?: string) { | ||
| super( | ||
| request, | ||
| response, | ||
| message || | ||
| `Request to ${request.url} failed with status code ${response.status}.`, | ||
| ); | ||
| this.name = 'ApiResponseError'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| /** | ||
| * Base class for all custom API-related errors. | ||
| */ | ||
| export class BaseError extends Error { | ||
| /** | ||
| * | ||
| * @param message The error message. | ||
| */ | ||
| constructor(message?: string) { | ||
| super(message); | ||
| this.name = 'BaseError'; | ||
| Object.setPrototypeOf(this, new.target.prototype); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import {BaseError} from './BaseError'; | ||
|
|
||
| /** | ||
| * Base class for HTTP-related errors. | ||
| */ | ||
| export class HttpError extends BaseError { | ||
| /** | ||
| * @param request The Request object that generated the error. | ||
| * @param response The raw HTTP Response object (guaranteed to be 2xx, e.g., 204). | ||
| * @param message The error message. | ||
| */ | ||
| constructor( | ||
| public readonly request: Request, | ||
| public readonly response?: Response, | ||
| message?: string | ||
| ) { | ||
| super(message); | ||
| this.name = 'HttpError'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import {HttpError} from './HttpError'; | ||
|
|
||
| /** | ||
| * Error thrown when a JSON response from the server cannot be parsed. | ||
| */ | ||
| export class JsonParseError extends HttpError { | ||
| /** | ||
| * @param request The Request object that generated the error. | ||
| * @param message The error message. | ||
| */ | ||
| constructor(request: Request, message?: string) { | ||
| super( | ||
| request, | ||
| undefined, | ||
| message || 'The server returned an invalid JSON response.' | ||
| ); | ||
| this.name = 'JsonParseError'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import {HttpError} from './HttpError'; | ||
|
|
||
| /** | ||
| * Error thrown when a request succeeds (i.e., status 2xx) | ||
| * but the response body contains no content or is not the expected JSON format. | ||
| * This typically corresponds to a 204 No Content status when content was expected. | ||
| */ | ||
| export class NoContentError extends HttpError { | ||
| /** | ||
| * @param request The Request object that was sent. | ||
| * @param response The Response object from the fetch call. | ||
| * @param message The error message. | ||
| */ | ||
| constructor(request: Request, response: Response, message?: string) { | ||
| super( | ||
| request, | ||
| response, | ||
| message || 'Operation succeeded but server returned no content.' | ||
| ); | ||
| this.name = 'NoContentError'; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import {BaseError} from './BaseError'; | ||
|
|
||
| /** | ||
| * Error thrown when data validation fails. | ||
| */ | ||
| export class ValidationError extends BaseError { | ||
| /** | ||
| * | ||
| * @param message The error message. | ||
| */ | ||
| constructor(message?: string) { | ||
| super(message); | ||
| this.name = 'ValidationError'; | ||
| Object.setPrototypeOf(this, new.target.prototype); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| export {BaseError as Base} from './BaseError'; | ||
| export {HttpError as Http} from './HttpError'; | ||
| export {ValidationError as Validation} from './ValidationError'; | ||
| export {ApiNetworkError as ApiNetwork} from './ApiNetworkError'; | ||
| export {ApiResponseError as ApiResponse} from './ApiResponseError'; | ||
| export {JsonParseError as JsonParse} from './JsonParseError'; | ||
| export {NoContentError as NoContent} from './NoContentError'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| declare const loris: any; | ||
| import {Query, QueryParam} from './Query'; | ||
| import {Errors} from '../'; | ||
|
|
||
| export interface ErrorContext { | ||
| key: string | number; // The key that triggered the custom message (e.g., 'ApiNetworkError' or 404) | ||
| request: Request, | ||
| response?: Response, | ||
| } | ||
|
|
||
| /** | ||
| * A basic client for making HTTP requests to a REST API endpoint. | ||
| */ | ||
| export class Client<T> { | ||
| protected baseURL: URL; | ||
| protected subEndpoint?: string; | ||
| /** | ||
| * Function to retrieve a custom error message for a given error context. | ||
| */ | ||
| public getErrorMessage: ( | ||
| key: string | number, | ||
| request: Request, | ||
| response?: Response | ||
| ) => string | undefined = () => undefined; | ||
|
|
||
| /** | ||
| * Creates a new API client instance. | ||
| * | ||
| * @param baseURL The base URL for the API requests. | ||
| */ | ||
| constructor(baseURL: string) { | ||
| const origin = window.location.origin; // always https://... | ||
| const full = `${origin}/${baseURL}/`; | ||
| this.baseURL = new URL(full); | ||
| } | ||
|
|
||
| /** | ||
| * Sets an optional sub-endpoint path. | ||
| * | ||
| * @param subEndpoint An optional endpoint segment to append to the baseURL. | ||
| */ | ||
| setSubEndpoint(subEndpoint: string): this { | ||
| this.subEndpoint = subEndpoint; | ||
| return this; | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * Fetches a collection of resources. | ||
| * | ||
| * @param query A Query object to build the URL query string. | ||
| */ | ||
| async get<U = T>(query?: Query): Promise<U[]> { | ||
| // 1. Determine the path to resolve | ||
| const relativePath = this.subEndpoint ? this.subEndpoint : ''; | ||
|
|
||
| // 2. Create the full URL object by resolving the path against this.baseURL. | ||
| const url = new URL(relativePath, this.baseURL); | ||
|
|
||
| // 3. Add Query Parameters using the URL object's searchParams | ||
| if (query) { | ||
| const params = new URLSearchParams(query.build()); | ||
| params.forEach((value, key) => { | ||
| url.searchParams.append(key, value); | ||
| }); | ||
| } | ||
|
|
||
| // 4. Use the final URL object for the fetch request. | ||
| return this.fetchJSON<U[]>(url, { | ||
| method: 'GET', | ||
| headers: {'Accept': 'application/json'}, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Fetches a list of unique labels for the resource type based on query parameters. | ||
| * | ||
| * @param {...QueryParam} params One or more QueryParam objects to filter the labels. | ||
| */ | ||
| async getLabels(...params: QueryParam[]): Promise<string[]> { | ||
| const query = new Query(); | ||
| params.forEach((param) => query.addParam(param)); | ||
| return this.get<string>(query.addField('label')); | ||
| } | ||
|
|
||
| /** | ||
| * Fetches a single resource by its ID. | ||
| * | ||
| * @param id The unique identifier of the resource to fetch. | ||
| */ | ||
| async getById(id: string): Promise<T> { | ||
| // 1. Resolve the ID as a path segment against the this.baseURL object. | ||
| const url = new URL(id, this.baseURL); | ||
|
|
||
| // 2. Pass the final URL string to fetchJSON | ||
| return this.fetchJSON<T>(url, { | ||
| method: 'GET', | ||
| headers: {'Accept': 'application/json'}, | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new resource on the server. | ||
| * | ||
| * @param data The resource data to be created. | ||
| * @param mapper An optional function to map the input data before sending. | ||
| */ | ||
| async create<U = T>(data: T, mapper?: (data: T) => U): Promise<T> { | ||
| const payload = mapper ? mapper(data) : data; | ||
| return this.fetchJSON<T>(this.baseURL, { | ||
| method: 'POST', | ||
| headers: {'Content-Type': 'application/json'}, | ||
| body: JSON.stringify(payload), | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Updates an existing resource on the server. | ||
| * | ||
| * @param id The unique identifier of the resource to update. | ||
| * @param data The new resource data. | ||
| */ | ||
| async update(id: string, data: T): Promise<T> { | ||
| // 1. Resolve the ID as a path segment against the this.baseURL object. | ||
| const url = new URL(id, this.baseURL); | ||
|
|
||
| // 2. Pass the final URL string to fetchJSON | ||
| return this.fetchJSON<T>(url, { | ||
| method: 'PUT', | ||
| headers: {'Content-Type': 'application/json'}, | ||
| body: JSON.stringify(data), | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Handles the actual fetching and JSON parsing, including error handling. | ||
| * | ||
| * @param url The URL to which the request will be made. | ||
| * @param options The Fetch API request initialization options. | ||
| */ | ||
| protected async fetchJSON<U>( | ||
| url: URL, | ||
| options: RequestInit | ||
| ): Promise<U> { | ||
| const request = new Request(url, options); | ||
| try { | ||
| const response = await fetch(request); | ||
|
|
||
| // 1. Handle HTTP status errors (e.g., 404, 500) | ||
| if (!response.ok) { | ||
| throw new Errors.ApiResponse( | ||
| request, | ||
| response, | ||
| this.getErrorMessage('ApiResponseError', request, response) | ||
| ); | ||
| } | ||
|
|
||
| // Handle responses with no content or non-JSON content | ||
| const contentType = response.headers.get('content-type'); | ||
| if (!contentType || !contentType.includes('application/json')) { | ||
| throw new Errors.NoContent( | ||
| request, | ||
| response, | ||
| this.getErrorMessage('NoContentError', request, response) | ||
| ); | ||
| } | ||
|
|
||
| // 2. Handle JSON parsing errors | ||
| try { | ||
| const data = await response.json(); | ||
| return data as U; | ||
| } catch (e) { | ||
| const message = this.getErrorMessage('JsonParseError', request); | ||
| throw new Errors.JsonParse(request, message); | ||
| } | ||
| } catch (error) { | ||
| // 3. Handle network errors (e.g., no internet) | ||
| if (error instanceof Errors.Http) { | ||
| throw error; // Re-throw our custom errors | ||
| } | ||
| const message = this.getErrorMessage('ApiNetworkError', request); | ||
| throw new Errors.ApiNetwork(request, message); | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+constructor(baseURL: string) {
${origin}/${baseURL}/;+}