@equinor/fusion-framework-module-http
Advanced tools
Comparing version 5.2.3 to 6.0.0
# Change Log | ||
## 6.0.0 | ||
### Major Changes | ||
- [#2181](https://github.com/equinor/fusion-framework/pull/2181) [`ba2379b`](https://github.com/equinor/fusion-framework/commit/ba2379b177f23ccc023894e36e50d7fc56c929c8) Thanks [@odinr](https://github.com/odinr)! - The `blob` and `blob# Change Log methods in the `HttpClient` class have been updated to provide a more robust and flexible API for fetching blob resources. | ||
1. The `blob` and `blob# Change Log methods now accept an optional `args`parameter of type`FetchRequestInit<T, TRequest, TResponse>`, where `T` is the type of the expected blob result. This allows consumers to customize the fetch request and response handling. | ||
2. The `blob` and `blob# Change Log methods now return a `Promise<T>`and`StreamResponse<T>`respectively, where`T` is the type of the expected blob result. This allows consumers to handle the blob data in a more type-safe manner. | ||
3. The `blobSelector` function has been updated to extract the filename (if available) from the `content-disposition` header and return it along with the blob data in a `BlobResult` object. | ||
4. If you were previously using the `blob` or `blob# Change Log methods and expecting a `Blob`result, you must now use the new`BlobResult` type, which includes the filename (if available) and the blob data. | ||
> [!WARNING] | ||
> This alters the return type of the `blob` and `blob# Change Log methods, which is a **breaking change**. | ||
Example: | ||
```typescript | ||
const blobResult = await httpClient.blob('/path/to/blob'); | ||
console.log(blobResult.filename); // 'example.pdf' | ||
console.log(blobResult.blob); // Blob instance | ||
``` | ||
1. If you were providing a custom selector function to the `blob` or `blob# Change Log methods, you can now use the new `BlobResult` type in your selector function. | ||
Example: | ||
```typescript | ||
const customBlobSelector = async ( | ||
response: Response, | ||
): Promise<{ filename: string; blob: Blob }> => { | ||
// Extract filename and blob from the response | ||
const { filename, blob } = await blobSelector(response); | ||
return { filename, blob }; | ||
}; | ||
const blobResult = await httpClient.blob('/path/to/blob', { selector: customBlobSelector }); | ||
console.log(blobResult.filename); // 'example.pdf' | ||
console.log(blobResult.blob); // Blob instance | ||
``` | ||
3. If you were using the `blob# Change Log method and expecting a `StreamResponse<Blob>`, you can now use the new `StreamResponse<T>`type, where`T` is the type of the expected blob result. | ||
Example: | ||
```typescript | ||
const blobStream = httpClient.blob$('/path/to/blob'); | ||
blobStream.subscribe((blobResult) => { | ||
console.log(blobResult.filename); // 'example.pdf' | ||
console.log(blobResult.blob); // Blob instance | ||
}); | ||
``` | ||
### Patch Changes | ||
- [#2196](https://github.com/equinor/fusion-framework/pull/2196) [`1e60919`](https://github.com/equinor/fusion-framework/commit/1e60919e83fb65528c88f604d7bd43299ec412e1) Thanks [@odinr](https://github.com/odinr)! - The `jsonSelector` function was not checking the error type in the `catch` block. | ||
This lead to not throwing the error with parsed data, but always throwing a parser error, where the correct error was `cause` in the `ErrorOptions` | ||
**BREAKING CHANGE:** | ||
If for some reason developers has catched the error and assumed the `cause` property would give the proper error data, this will no longer be the case. | ||
```ts | ||
try { | ||
await jsonSelector(response); | ||
} catch (error) { | ||
if (error instanceof HttpJsonResponseError) { | ||
const { data, cause } = error; | ||
if (data) { | ||
console.error('the request was not `ok`, see provided error data', data); | ||
} else { | ||
console.error('failed to parse data from response, see provided cause', cause); | ||
} | ||
} | ||
} | ||
``` | ||
```diff | ||
try { | ||
await jsonSelector(response); | ||
} catch (error) { | ||
if(error instanceof HttpJsonResponseError) { | ||
- const data = error.cause instanceof HttpJsonResponseError ? err.cause.data : null; | ||
+ const data = error instanceof HttpJsonResponseError ? error.data : null; | ||
if(data) { | ||
console.error('the request was not `ok`, see provided error data', data); | ||
} else { | ||
console.error('failed to parse data from response, see provided cause', error.cause); | ||
} | ||
} | ||
} | ||
``` | ||
## 5.2.3 | ||
@@ -4,0 +96,0 @@ |
@@ -62,3 +62,4 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
const selector = (_a = args === null || args === void 0 ? void 0 : args.selector) !== null && _a !== void 0 ? _a : blobSelector; | ||
return this.fetch$(path, Object.assign(Object.assign({}, args), { selector })); | ||
const init = Object.assign(Object.assign({}, args), { selector }); | ||
return this.fetch$(path, init); | ||
} | ||
@@ -65,0 +66,0 @@ blob(path, args) { |
@@ -1,2 +0,12 @@ | ||
export const blobSelector = (response) => { | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
export const blobSelector = (response) => __awaiter(void 0, void 0, void 0, function* () { | ||
var _a, _b, _c; | ||
if (!response.ok) { | ||
@@ -8,4 +18,7 @@ throw new Error('network response was not OK'); | ||
} | ||
const filename = (_c = (_b = (_a = response.headers | ||
.get('content-disposition')) === null || _a === void 0 ? void 0 : _a.split(';').find((n) => n.includes('filename='))) === null || _b === void 0 ? void 0 : _b.replace('filename=', '')) === null || _c === void 0 ? void 0 : _c.trim(); | ||
try { | ||
return response.blob(); | ||
const blob = yield response.blob(); | ||
return { filename, blob }; | ||
} | ||
@@ -15,4 +28,4 @@ catch (err) { | ||
} | ||
}; | ||
}); | ||
export default blobSelector; | ||
//# sourceMappingURL=blob-selector.js.map |
@@ -23,2 +23,5 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
catch (cause) { | ||
if (cause instanceof HttpJsonResponseError) { | ||
throw cause; | ||
} | ||
throw new HttpJsonResponseError('failed to parse response', response, { cause }); | ||
@@ -25,0 +28,0 @@ } |
@@ -1,2 +0,2 @@ | ||
export const version = '5.2.3'; | ||
export const version = '6.0.0'; | ||
//# sourceMappingURL=version.js.map |
import { Subject } from 'rxjs'; | ||
import type { Observable, ObservableInput } from 'rxjs'; | ||
import type { IHttpRequestHandler, IHttpResponseHandler } from '../operators'; | ||
import type { FetchRequest, FetchRequestInit, FetchResponse, IHttpClient, JsonRequest, StreamResponse } from '.'; | ||
import type { BlobResult, FetchRequest, FetchRequestInit, FetchResponse, IHttpClient, JsonRequest, StreamResponse } from './types'; | ||
export type HttpClientCreateOptions<TRequest extends FetchRequest = FetchRequest, TResponse = Response> = { | ||
@@ -25,4 +25,4 @@ requestHandler: IHttpRequestHandler<TRequest>; | ||
json<T = unknown>(path: string, args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>): Promise<T>; | ||
blob$(path: string, args?: FetchRequestInit<Blob, TRequest, TResponse>): StreamResponse<Blob>; | ||
blob(path: string, args?: FetchRequestInit<Blob, TRequest, TResponse>): Promise<Blob>; | ||
blob$<T = BlobResult>(path: string, args?: FetchRequestInit<T, TRequest, TResponse>): StreamResponse<T>; | ||
blob<T = BlobResult>(path: string, args?: FetchRequestInit<T, TRequest, TResponse>): Promise<T>; | ||
jsonAsync<T = unknown>(path: string, args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>): Promise<T>; | ||
@@ -29,0 +29,0 @@ execute<T = TResponse, TMethod extends 'fetch' | 'fetch$' | 'json' | 'json$' = 'fetch'>(method: TMethod, path: string, init?: FetchRequestInit<T, TRequest, TResponse>): ReturnType<IHttpClient[TMethod]>; |
@@ -11,2 +11,6 @@ import type { ObservableInput, Observable } from 'rxjs'; | ||
}; | ||
export type BlobResult = { | ||
filename?: string; | ||
blob: Blob; | ||
}; | ||
export type FetchResponse<T = unknown> = Response & { | ||
@@ -34,5 +38,5 @@ json(): Promise<T>; | ||
jsonAsync<T = unknown>(path: string, args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>): Promise<T>; | ||
blob(path: string, args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>): Promise<Blob>; | ||
blob$(path: string, args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>): StreamResponse<Blob>; | ||
blob<T = BlobResult>(path: string, args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>): Promise<T>; | ||
blob$<T = BlobResult>(path: string, args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>): StreamResponse<T>; | ||
abort(): void; | ||
} |
@@ -1,2 +0,3 @@ | ||
export declare const blobSelector: <TResponse extends Response = Response>(response: TResponse) => Promise<Blob>; | ||
import { type BlobResult } from '../client/types'; | ||
export declare const blobSelector: <TResponse extends Response = Response>(response: TResponse) => Promise<BlobResult>; | ||
export default blobSelector; |
@@ -1,1 +0,1 @@ | ||
export declare const version = "5.2.3"; | ||
export declare const version = "6.0.0"; |
{ | ||
"name": "@equinor/fusion-framework-module-http", | ||
"version": "5.2.3", | ||
"version": "6.0.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/esm/index.js", |
@@ -11,2 +11,3 @@ import { firstValueFrom, of, Subject } from 'rxjs'; | ||
import type { | ||
BlobResult, | ||
FetchRequest, | ||
@@ -18,3 +19,3 @@ FetchRequestInit, | ||
StreamResponse, | ||
} from '.'; | ||
} from './types'; | ||
import { HttpResponseError } from '../../errors'; | ||
@@ -116,14 +117,37 @@ | ||
public blob$( | ||
/** | ||
* Fetches a blob resource from the specified path and returns a stream response. | ||
* | ||
* @param path - The path to the blob resource. | ||
* @param args - Optional request initialization options, including a custom selector function. | ||
* @returns A stream response containing the fetched blob data. | ||
*/ | ||
public blob$<T = BlobResult>( | ||
path: string, | ||
args?: FetchRequestInit<Blob, TRequest, TResponse>, | ||
): StreamResponse<Blob> { | ||
args?: FetchRequestInit<T, TRequest, TResponse>, | ||
): StreamResponse<T> { | ||
// Get the selector value from the provided args, or use the default blobSelector | ||
const selector = args?.selector ?? blobSelector; | ||
return this.fetch$(path, { | ||
// Create the FetchRequestInit object with the provided args and the selector | ||
const init = { | ||
...args, | ||
selector, | ||
} as FetchRequestInit<Blob, TRequest, TResponse>); | ||
} as FetchRequestInit<T, TRequest, TResponse>; | ||
// Call the fetch$ method with the provided path and the constructed init object | ||
return this.fetch$(path, init); | ||
} | ||
public blob(path: string, args?: FetchRequestInit<Blob, TRequest, TResponse>): Promise<Blob> { | ||
/** | ||
* Fetches a blob from the specified path and returns a Promise that resolves to the blob result. | ||
* | ||
* @param path - The path to fetch the blob from. | ||
* @param args - Optional arguments for the fetch request, including request body, headers, and response type. | ||
* @returns A Promise that resolves to the blob result. | ||
*/ | ||
public blob<T = BlobResult>( | ||
path: string, | ||
args?: FetchRequestInit<T, TRequest, TResponse>, | ||
): Promise<T> { | ||
return firstValueFrom(this.blob$(path, args)); | ||
@@ -130,0 +154,0 @@ } |
@@ -15,2 +15,9 @@ import type { ObservableInput, Observable } from 'rxjs'; | ||
/** | ||
* Represents the result of a blob operation, including the filename (if available) and the blob itself. | ||
* @property {string} [filename] - The filename of the blob, if available. | ||
* @property {Blob} blob - The blob data. | ||
*/ | ||
export type BlobResult = { filename?: string; blob: Blob }; | ||
export type FetchResponse<T = unknown> = Response & { | ||
@@ -153,11 +160,25 @@ json(): Promise<T>; | ||
blob( | ||
/** | ||
* Fetches a blob resource from the specified path. | ||
* | ||
* @param path - The path to the blob resource. | ||
* @param args - Optional arguments for the fetch request, including the request body, headers, and response type. | ||
* @returns A Promise that resolves to the fetched blob data. | ||
*/ | ||
blob<T = BlobResult>( | ||
path: string, | ||
args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>, | ||
): Promise<Blob>; | ||
args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>, | ||
): Promise<T>; | ||
blob$( | ||
/** | ||
* Fetches a blob from the specified path and returns a stream response. | ||
* | ||
* @param path - The path to fetch the blob from. | ||
* @param args - Optional arguments for the fetch request, including the request body, headers, and response type. | ||
* @returns A stream response containing the fetched blob. | ||
*/ | ||
blob$<T = BlobResult>( | ||
path: string, | ||
args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>, | ||
): StreamResponse<Blob>; | ||
args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>, | ||
): StreamResponse<T>; | ||
@@ -164,0 +185,0 @@ /** |
@@ -1,8 +0,19 @@ | ||
export const blobSelector = <TResponse extends Response = Response>( | ||
import { type BlobResult } from '../client/types'; | ||
/** | ||
* Extracts a blob and filename from a successful HTTP response. | ||
* | ||
* @param response - The HTTP response to extract the blob and filename from. | ||
* @returns A promise that resolves to an object containing the extracted blob and filename. | ||
* @throws {Error} If the response is not successful or if there is an error parsing the response. | ||
*/ | ||
export const blobSelector = async <TResponse extends Response = Response>( | ||
response: TResponse, | ||
): Promise<Blob> => { | ||
): Promise<BlobResult> => { | ||
if (!response.ok) { | ||
// Throw an error if the network response is not successful | ||
throw new Error('network response was not OK'); | ||
} | ||
//Status code 204 is no content | ||
// Status code 204 indicates no content, so throw an error | ||
if (response.status === 204) { | ||
@@ -12,5 +23,16 @@ throw new Error('no content'); | ||
// Extract the filename from the 'content-disposition' header | ||
const filename = response.headers | ||
.get('content-disposition') | ||
?.split(';') | ||
.find((n) => n.includes('filename=')) | ||
?.replace('filename=', '') | ||
?.trim(); | ||
try { | ||
return response.blob(); | ||
// Convert the response to a Blob and return the filename and Blob | ||
const blob = await response.blob(); | ||
return { filename, blob }; | ||
} catch (err) { | ||
// Throw an error if there's a problem parsing the response | ||
throw Error('failed to parse response'); | ||
@@ -17,0 +39,0 @@ } |
import { HttpJsonResponseError } from '../../errors'; | ||
/** | ||
* Converts the JSON response from an HTTP request into the specified type. | ||
* If the response is not OK or if there is an error parsing the response, an error is thrown. | ||
* If the response has a status code of 204 (No Content), a resolved Promise is returned. | ||
* Asynchronously parses the JSON data from a given HTTP response. | ||
* | ||
* @param response The HTTP response object. | ||
* @returns A Promise that resolves to the parsed JSON data. | ||
* @throws {Error} If the network response is not OK. | ||
* @throws {HttpJsonResponseError} If there is an error parsing the response. | ||
* If the response has a status code of 204 (No Content), this function will resolve with `undefined`. | ||
* | ||
* If the response is not successful (i.e. `response.ok` is `false`), this function will throw an `HttpJsonResponseError` with the response details and the parsed data (if any). | ||
* | ||
* If there is an error parsing the JSON data, this function will throw an `HttpJsonResponseError` with the parsing error and the original response. | ||
* | ||
* @template TType - The expected type of the parsed JSON data. | ||
* @template TResponse - The type of the HTTP response object. | ||
* @param response - The HTTP response to parse. | ||
* @returns A promise that resolves with the parsed JSON data, or rejects with an `HttpJsonResponseError`. | ||
*/ | ||
@@ -16,3 +20,3 @@ export const jsonSelector = async <TType = unknown, TResponse extends Response = Response>( | ||
): Promise<TType> => { | ||
/** Status code 204 is no content */ | ||
/** Status code 204 indicates no content in the response */ | ||
if (response.status === 204) { | ||
@@ -23,8 +27,20 @@ return Promise.resolve() as Promise<TType>; | ||
try { | ||
// Parse the response JSON data | ||
const data = await response.json(); | ||
// Check if the response was successful | ||
if (!response.ok) { | ||
// Throw an error with the response details | ||
throw new HttpJsonResponseError('network response was not OK', response, { data }); | ||
} | ||
// Return the parsed data | ||
return data; | ||
} catch (cause) { | ||
// If the cause is an HttpJsonResponseError, rethrow it | ||
if (cause instanceof HttpJsonResponseError) { | ||
throw cause; | ||
} | ||
// Otherwise, throw a new HttpJsonResponseError with the parsing error | ||
throw new HttpJsonResponseError('failed to parse response', response, { cause }); | ||
@@ -31,0 +47,0 @@ } |
// Generated by genversion. | ||
export const version = '5.2.3'; | ||
export const version = '6.0.0'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
241683
1662