mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat(stock-info): implement request batching with BatchingResource
- Add BatchingResource base class for automatic API request batching - Refactor StockInfoComponent to use StockInfoResource with batching - Remove redundant StockResource provider from RewardShoppingCartItemComponent - Update tests to match new BatchingResourceRef API - Add comprehensive documentation for BatchingResource pattern The BatchingResource pattern optimizes multiple simultaneous stock info requests by collecting params within a 250ms window and making a single batched API call, significantly reducing network overhead.
This commit is contained in:
@@ -21,7 +21,6 @@ import { TabService } from '@isa/core/tabs';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { RewardShoppingCartItemQuantityControlComponent } from './reward-shopping-cart-item-quantity-control.component';
|
||||
import { RewardShoppingCartItemRemoveButtonComponent } from './reward-shopping-cart-item-remove-button.component';
|
||||
import { StockInfoResource } from '@isa/remission/data-access';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import { isaOtherInfo } from '@isa/icons';
|
||||
|
||||
@@ -42,7 +41,7 @@ import { isaOtherInfo } from '@isa/icons';
|
||||
RewardShoppingCartItemRemoveButtonComponent,
|
||||
NgIcon,
|
||||
],
|
||||
providers: [],
|
||||
providers: [provideIcons({ isaOtherInfo })],
|
||||
})
|
||||
export class RewardShoppingCartItemComponent {
|
||||
#logger = logger(() => ({ component: 'RewardShoppingCartItemComponent' }));
|
||||
|
||||
@@ -34,7 +34,6 @@ export type BatchingResourceRef<TResourceValue> = {
|
||||
reload: () => void;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generic batching resource for optimizing multiple API requests.
|
||||
* Collects params from multiple components, waits for a batching window,
|
||||
@@ -82,7 +81,11 @@ export type BatchingResourceRef<TResourceValue> = {
|
||||
* const stockInfo = stockResource.resource(itemId);
|
||||
* const inStock = computed(() => stockInfo.value()?.inStock ?? 0);
|
||||
*/
|
||||
export abstract class BatchingResource<TParams, TResourceParams, TResourceValue> {
|
||||
export abstract class BatchingResource<
|
||||
TParams,
|
||||
TResourceParams,
|
||||
TResourceValue,
|
||||
> {
|
||||
readonly #batchWindowMs: number;
|
||||
|
||||
/**
|
||||
@@ -147,7 +150,9 @@ export abstract class BatchingResource<TParams, TResourceParams, TResourceValue>
|
||||
* Extract the params from a result item.
|
||||
* Implement this method to match results back to requested params.
|
||||
*/
|
||||
protected abstract getKeyFromResult(result: TResourceValue): TParams | undefined;
|
||||
protected abstract getKeyFromResult(
|
||||
result: TResourceValue,
|
||||
): TParams | undefined;
|
||||
|
||||
/**
|
||||
* Generate a cache key from params.
|
||||
@@ -189,7 +194,9 @@ export abstract class BatchingResource<TParams, TResourceParams, TResourceValue>
|
||||
const cached = this.#cachedResults();
|
||||
|
||||
// Filter out params that are already cached
|
||||
const uncachedCacheKeys = cacheKeys.filter((cacheKey) => !cached.has(cacheKey));
|
||||
const uncachedCacheKeys = cacheKeys.filter(
|
||||
(cacheKey) => !cached.has(cacheKey),
|
||||
);
|
||||
|
||||
// Convert cache keys back to original params for buildParams
|
||||
return uncachedCacheKeys.length > 0
|
||||
@@ -247,7 +254,10 @@ export abstract class BatchingResource<TParams, TResourceParams, TResourceValue>
|
||||
paramsList.forEach((itemParams) => {
|
||||
const hasResult = results.some((r) => {
|
||||
const resultKey = this.getKeyFromResult(r);
|
||||
return resultKey !== undefined && this.getCacheKey(resultKey) === this.getCacheKey(itemParams);
|
||||
return (
|
||||
resultKey !== undefined &&
|
||||
this.getCacheKey(resultKey) === this.getCacheKey(itemParams)
|
||||
);
|
||||
});
|
||||
if (!hasResult) {
|
||||
const cacheKey = this.getCacheKey(itemParams);
|
||||
|
||||
Reference in New Issue
Block a user