diff --git a/lib/src/recommender.ts b/lib/src/recommender.ts index ef7d7b9..37a58ed 100644 --- a/lib/src/recommender.ts +++ b/lib/src/recommender.ts @@ -1,4 +1,4 @@ -import { RelewiseClient, RelewiseClientOptions } from './relewise.client'; +import { RelewiseClient, RelewiseClientOptions, RelewiseRequestOptions } from './relewise.client'; import { ProductRecommendationResponse, ContentRecommendationResponse, @@ -46,139 +46,139 @@ export class Recommender extends RelewiseClient { super(datasetId, apiKey, options); } - public async recommendPopularSearchTerms(request: PopularSearchTermsRecommendationRequest): Promise { - return this.request('PopularSearchTermsRecommendationRequest', request); + public async recommendPopularSearchTerms(request: PopularSearchTermsRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularSearchTermsRecommendationRequest', request, options); } //#region Brands - public async recommendPersonalBrands(request: PersonalBrandRecommendationRequest): Promise { - return this.request('PersonalBrandRecommendationRequest', request); + public async recommendPersonalBrands(request: PersonalBrandRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PersonalBrandRecommendationRequest', request, options); } - public async recommendPopularBrands(request: PopularBrandsRecommendationRequest): Promise { - return this.request('PopularBrandsRecommendationRequest', request); + public async recommendPopularBrands(request: PopularBrandsRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularBrandsRecommendationRequest', request, options); } //#endregion //#region Content Categories - public async recommendPersonalContentCategories(request: PersonalContentCategoryRecommendationRequest): Promise { - return this.request('PersonalContentCategoryRecommendationRequest', request); + public async recommendPersonalContentCategories(request: PersonalContentCategoryRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PersonalContentCategoryRecommendationRequest', request, options); } - public async recommendPopularContentCategories(request: PopularContentCategoriesRecommendationRequest): Promise { - return this.request('PopularContentCategoriesRecommendationRequest', request); + public async recommendPopularContentCategories(request: PopularContentCategoriesRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularContentCategoriesRecommendationRequest', request, options); } //#endregion //#region Product Categories - public async recommendPersonalProductCategories(request: PersonalProductCategoryRecommendationRequest): Promise { - return this.request('PersonalProductCategoryRecommendationRequest', request); + public async recommendPersonalProductCategories(request: PersonalProductCategoryRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PersonalProductCategoryRecommendationRequest', request, options); } - public async recommendPopularProductCategories(request: PopularProductCategoriesRecommendationRequest): Promise { - return this.request('PopularProductCategoriesRecommendationRequest', request); + public async recommendPopularProductCategories(request: PopularProductCategoriesRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularProductCategoriesRecommendationRequest', request, options); } //#endregion //#region Products - public async recommendPurchasedWithProduct(request: PurchasedWithProductRequest): Promise { - return this.request('PurchasedWithProductRequest', request); + public async recommendPurchasedWithProduct(request: PurchasedWithProductRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PurchasedWithProductRequest', request, options); } - public async recommendPurchasedWithMultipleProducts(request: PurchasedWithMultipleProductsRequest): Promise { - return this.request('PurchasedWithMultipleProductsRequest', request); + public async recommendPurchasedWithMultipleProducts(request: PurchasedWithMultipleProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PurchasedWithMultipleProductsRequest', request, options); } - public async sortVariants(request: SortVariantsRequest): Promise { - return this.request('SortVariantsRequest', request); + public async sortVariants(request: SortVariantsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('SortVariantsRequest', request, options); } - public async sortProducts(request: SortProductsRequest): Promise { - return this.request('SortProductsRequest', request); + public async sortProducts(request: SortProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('SortProductsRequest', request, options); } - public async recommendSimilarProducts(request: SimilarProductsRequest): Promise { - return this.request('SimilarProductsRequest', request); + public async recommendSimilarProducts(request: SimilarProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('SimilarProductsRequest', request, options); } - public async recommendSearchTermBasedProducts(request: SearchTermBasedProductRecommendationRequest): Promise { - return this.request('SearchTermBasedProductRecommendationRequest', request); + public async recommendSearchTermBasedProducts(request: SearchTermBasedProductRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('SearchTermBasedProductRecommendationRequest', request, options); } - public async recentlyViewedProducts(request: RecentlyViewedProductsRequest): Promise { - return this.request('RecentlyViewedProductsRequest', request); + public async recentlyViewedProducts(request: RecentlyViewedProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('RecentlyViewedProductsRequest', request, options); } - public async recommendPurchasedWithCurrentCart(request: PurchasedWithCurrentCartRequest): Promise { - return this.request('PurchasedWithCurrentCartRequest', request); + public async recommendPurchasedWithCurrentCart(request: PurchasedWithCurrentCartRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PurchasedWithCurrentCartRequest', request, options); } - public async recommendProductsViewedAfterViewingProduct(request: ProductsViewedAfterViewingProductRequest): Promise { - return this.request('ProductsViewedAfterViewingProductRequest', request); + public async recommendProductsViewedAfterViewingProduct(request: ProductsViewedAfterViewingProductRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ProductsViewedAfterViewingProductRequest', request, options); } - public async recommendProductsViewedAfterViewingContent(request: ProductsViewedAfterViewingContentRequest): Promise { - return this.request('ProductsViewedAfterViewingContentRequest', request); + public async recommendProductsViewedAfterViewingContent(request: ProductsViewedAfterViewingContentRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ProductsViewedAfterViewingContentRequest', request, options); } - public async recommendPopularProducts(request: PopularProductsRequest): Promise { - return this.request('PopularProductsRequest', request); + public async recommendPopularProducts(request: PopularProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularProductsRequest', request, options); } - public async recommendPersonalProducts(request: PersonalProductRecommendationRequest): Promise { - return this.request('PersonalProductRecommendationRequest', request); + public async recommendPersonalProducts(request: PersonalProductRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PersonalProductRecommendationRequest', request, options); } //#endregion //#region Content - public async recommendPopularContents(request: PopularContentsRequest): Promise { - return this.request('PopularContentsRequest', request); + public async recommendPopularContents(request: PopularContentsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularContentsRequest', request, options); } - public async recommendPersonalContents(request: PersonalContentRecommendationRequest): Promise { - return this.request('PopularContentsRequest', request); + public async recommendPersonalContents(request: PersonalContentRecommendationRequest, options?: RelewiseRequestOptions): Promise { + return this.request('PopularContentsRequest', request, options); } - public async recommendContentsViewedAfterViewingProduct(request: ContentsViewedAfterViewingProductRequest): Promise { - return this.request('ContentsViewedAfterViewingProductRequest', request); + public async recommendContentsViewedAfterViewingProduct(request: ContentsViewedAfterViewingProductRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ContentsViewedAfterViewingProductRequest', request, options); } - public async recommendContentsViewedAfterViewingMultipleProducts(request: ContentsViewedAfterViewingMultipleProductsRequest): Promise { - return this.request('ContentsViewedAfterViewingProductRequest', request); + public async recommendContentsViewedAfterViewingMultipleProducts(request: ContentsViewedAfterViewingMultipleProductsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ContentsViewedAfterViewingProductRequest', request, options); } - public async recommendContentsViewedAfterViewingMultipleContents(request: ContentsViewedAfterViewingMultipleContentsRequest): Promise { - return this.request('ContentsViewedAfterViewingMultipleContentsRequest', request); + public async recommendContentsViewedAfterViewingMultipleContents(request: ContentsViewedAfterViewingMultipleContentsRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ContentsViewedAfterViewingMultipleContentsRequest', request, options); } - public async recommendContentsViewedAfterViewingContent(request: ContentsViewedAfterViewingContentRequest): Promise { - return this.request('ContentsViewedAfterViewingContentRequest', request); + public async recommendContentsViewedAfterViewingContent(request: ContentsViewedAfterViewingContentRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ContentsViewedAfterViewingContentRequest', request, options); } //#endregion //#region Batching - public async batchProductRecommendations(request: ProductRecommendationRequestCollection): Promise { - return this.request('ProductRecommendationRequestCollection', request); + public async batchProductRecommendations(request: ProductRecommendationRequestCollection, options?: RelewiseRequestOptions): Promise { + return this.request('ProductRecommendationRequestCollection', request, options); } - public async batchContentRecommendations(request: ContentRecommendationRequestCollection): Promise { - return this.request('ContentRecommendationRequestCollection', request); + public async batchContentRecommendations(request: ContentRecommendationRequestCollection, options?: RelewiseRequestOptions): Promise { + return this.request('ContentRecommendationRequestCollection', request, options); } - public async batchContentCategoryRecommendations(request: ContentCategoryRecommendationRequestCollection): Promise { - return this.request('ContentCategoryRecommendationRequestCollection', request); + public async batchContentCategoryRecommendations(request: ContentCategoryRecommendationRequestCollection, options?: RelewiseRequestOptions): Promise { + return this.request('ContentCategoryRecommendationRequestCollection', request, options); } - public async batchProductCategoryRecommendations(request: ProductCategoryRecommendationRequestCollection): Promise { - return this.request('ProductCategoryRecommendationRequestCollection', request); + public async batchProductCategoryRecommendations(request: ProductCategoryRecommendationRequestCollection, options?: RelewiseRequestOptions): Promise { + return this.request('ProductCategoryRecommendationRequestCollection', request, options); } //#endregion diff --git a/lib/src/relewise.client.ts b/lib/src/relewise.client.ts index 786f7f5..0630322 100644 --- a/lib/src/relewise.client.ts +++ b/lib/src/relewise.client.ts @@ -5,6 +5,10 @@ export interface RelewiseClientOptions { serverUrl?: string; } +export interface RelewiseRequestOptions { + abortSignal?: AbortSignal +} + export class ProblemDetailsError extends Error { private _details?: HttpProblemDetails; @@ -30,11 +34,14 @@ export interface HttpProblemDetails { export abstract class RelewiseClient { private readonly _serverUrl: string = 'https://api.relewise.com'; private readonly _urlPath: string = 'v1'; + private readonly _apiKeyHeader: string; constructor(protected readonly datasetId: string, protected readonly apiKey: string, options?: RelewiseClientOptions) { if (!datasetId) throw new Error('Dataset id cannot be null or empty. Please contact Relewise if you don\'t have an account already or would like a free demo license'); if (!apiKey) throw new Error('API Key secret cannot be null or empty. Please contact Relewise support if you don\'t know the apiKeySecret for your datasetId.'); + this._apiKeyHeader = `APIKey ${this.apiKey}`; + if (options?.serverUrl) { this._serverUrl = options.serverUrl; } @@ -44,23 +51,26 @@ export abstract class RelewiseClient { return this._serverUrl; } - protected async request(name: string, data: TRequest): Promise { - const apiKeyHeader = `APIKey ${this.apiKey}`; + protected async request(name: string, data: TRequest, options?: RelewiseRequestOptions): Promise { const requestUrl = this.createRequestUrl(this._serverUrl, this.datasetId, this._urlPath, name); const response = await fetch(requestUrl, { method: 'POST', headers: { - Authorization: apiKeyHeader, + Authorization: this._apiKeyHeader, 'Content-Type': 'application/json', 'X-Relewise-Version': version.tag, }, body: JSON.stringify(data), + signal: options?.abortSignal, }); if (!response.ok) { let responseMessage = null; - try { responseMessage = await response.json(); } catch (_) { console.log(responseMessage)} + try { + responseMessage = await response.json(); + } catch (_) { + } throw new ProblemDetailsError('Error when calling the Relewise API. Read more in the details property if there is error response or look in the network tab.', responseMessage); } diff --git a/lib/src/searcher.ts b/lib/src/searcher.ts index 6f9d5d4..47542c8 100644 --- a/lib/src/searcher.ts +++ b/lib/src/searcher.ts @@ -1,4 +1,4 @@ -import { RelewiseClient, RelewiseClientOptions } from './relewise.client'; +import { RelewiseClient, RelewiseClientOptions, RelewiseRequestOptions } from './relewise.client'; import { SearchRequestCollection, SearchResponseCollection, @@ -17,23 +17,23 @@ export class Searcher extends RelewiseClient { super(datasetId, apiKey, options); } - public async searchProducts(request: ProductSearchRequest): Promise { - return this.request('ProductSearchRequest', request); + public async searchProducts(request: ProductSearchRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ProductSearchRequest', request, options); } - public async searchProductCategories(request: ProductCategorySearchRequest): Promise { - return this.request('ProductCategorySearchRequest', request); + public async searchProductCategories(request: ProductCategorySearchRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ProductCategorySearchRequest', request, options); } - public async searchContents(request: ContentSearchRequest): Promise { - return this.request('ContentSearchRequest', request); + public async searchContents(request: ContentSearchRequest, options?: RelewiseRequestOptions): Promise { + return this.request('ContentSearchRequest', request, options); } - public async searchTermPrediction(request: SearchTermPredictionRequest): Promise { - return this.request('SearchTermPredictionRequest', request); + public async searchTermPrediction(request: SearchTermPredictionRequest, options?: RelewiseRequestOptions): Promise { + return this.request('SearchTermPredictionRequest', request, options); } - public async batch(requestCollections: SearchRequestCollection): Promise { - return this.request('SearchRequestCollection', requestCollections); + public async batch(requestCollections: SearchRequestCollection, options?: RelewiseRequestOptions): Promise { + return this.request('SearchRequestCollection', requestCollections, options); } } \ No newline at end of file diff --git a/lib/src/tracker.ts b/lib/src/tracker.ts index 9dcaf34..1351bd4 100644 --- a/lib/src/tracker.ts +++ b/lib/src/tracker.ts @@ -1,4 +1,4 @@ -import { RelewiseClient, RelewiseClientOptions } from './relewise.client'; +import { RelewiseClient, RelewiseClientOptions, RelewiseRequestOptions } from './relewise.client'; import { TrackOrderRequest, TrackCartRequest, TrackProductViewRequest, TrackProductCategoryViewRequest, TrackContentViewRequest, TrackContentCategoryViewRequest, TrackBrandViewRequest, User, TrackSearchTermRequest, TrackUserUpdateRequest, DataValue, @@ -18,7 +18,7 @@ export class Tracker extends RelewiseClient { trackingNumber?: string, lineItems: { productId: string, variantId?: string, lineTotal: number, quantity: number }[], cartName?: string - }): Promise { + }, options?: RelewiseRequestOptions): Promise { return this.request('TrackOrderRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackOrderRequest, Relewise.Client', order: { @@ -37,7 +37,7 @@ export class Tracker extends RelewiseClient { cartName: cartName, user: user, }, - }); + }, options); } public async trackCart({ user, subtotal, lineItems, data, cartName = 'default' }: { @@ -46,7 +46,7 @@ export class Tracker extends RelewiseClient { lineItems: { productId: string, variantId?: string, lineTotal: number, quantity: number }[], data?: Record, cartName?: string - }): Promise { + }, options?: RelewiseRequestOptions): Promise { return this.request('TrackCartRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackCartRequest, Relewise.Client', cart: { @@ -64,10 +64,10 @@ export class Tracker extends RelewiseClient { user: user, data: data, }, - }); + }, options); } - public async trackProductView({ productId, variantId, user }: { productId: string, variantId?: string, user: User }): Promise { + public async trackProductView({ productId, variantId, user }: { productId: string, variantId?: string, user: User }, options?: RelewiseRequestOptions): Promise { return this.request('TrackProductViewRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackProductViewRequest, Relewise.Client', productView: { @@ -78,10 +78,10 @@ export class Tracker extends RelewiseClient { ...(variantId && { variant: { id: variantId }}), user: user, }, - }); + }, options); } - public async trackProductCategoryView({ idPath, user }: { idPath: string[], user: User }): Promise { + public async trackProductCategoryView({ idPath, user }: { idPath: string[], user: User }, options?: RelewiseRequestOptions): Promise { return this.request('TrackProductCategoryViewRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackProductCategoryViewRequest, Relewise.Client', productCategoryView: { @@ -89,10 +89,10 @@ export class Tracker extends RelewiseClient { idPath: idPath, user: user, }, - }); + }, options); } - public async trackContentView({ contentId, user }: { contentId: string, user: User }): Promise { + public async trackContentView({ contentId, user }: { contentId: string, user: User }, options?: RelewiseRequestOptions): Promise { return this.request('TrackContentViewRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackContentViewRequest, Relewise.Client', contentView: { @@ -102,10 +102,10 @@ export class Tracker extends RelewiseClient { }, user: user, }, - }); + }, options); } - public async trackContentCategoryView({ idPath, user }: { idPath: string[], user: User }): Promise { + public async trackContentCategoryView({ idPath, user }: { idPath: string[], user: User }, options?: RelewiseRequestOptions): Promise { return this.request('TrackContentCategoryViewRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackContentCategoryViewRequest, Relewise.Client', contentCategoryView: { @@ -113,10 +113,10 @@ export class Tracker extends RelewiseClient { idPath: idPath, user: user, }, - }); + }, options); } - public async trackBrandView({ brandId, user }: { brandId: string, user: User }): Promise { + public async trackBrandView({ brandId, user }: { brandId: string, user: User }, options?: RelewiseRequestOptions): Promise { return this.request('TrackBrandViewRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackBrandViewRequest, Relewise.Client', brandView: { @@ -126,10 +126,10 @@ export class Tracker extends RelewiseClient { }, user: user, }, - }); + }, options); } - public async trackSearchTerm({ term, language, user }: { term: string, user: User, language: string }): Promise { + public async trackSearchTerm({ term, language, user }: { term: string, user: User, language: string }, options?: RelewiseRequestOptions): Promise { return this.request('TrackSearchTermRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackSearchTermRequest, Relewise.Client', searchTerm: { @@ -140,10 +140,10 @@ export class Tracker extends RelewiseClient { term: term, user: user, }, - }); + }, options); } - public async trackUserUpdate({ user, updateKind = 'UpdateAndAppend' }: { user: User, updateKind?: 'None' | 'UpdateAndAppend' | 'ReplaceProvidedProperties' | 'ClearAndReplace' }): Promise { + public async trackUserUpdate({ user, updateKind = 'UpdateAndAppend' }: { user: User, updateKind?: 'None' | 'UpdateAndAppend' | 'ReplaceProvidedProperties' | 'ClearAndReplace' }, options?: RelewiseRequestOptions): Promise { return this.request('TrackUserUpdateRequest', { $type: 'Relewise.Client.Requests.Tracking.TrackUserUpdateRequest, Relewise.Client', userUpdate: { @@ -151,6 +151,6 @@ export class Tracker extends RelewiseClient { user: user, kind: updateKind, }, - }); + }, options); } } diff --git a/lib/tests/integration-tests/filters.integration.test.ts b/lib/tests/integration-tests/filters.integration.test.ts index ed45bbd..3ed72c5 100644 --- a/lib/tests/integration-tests/filters.integration.test.ts +++ b/lib/tests/integration-tests/filters.integration.test.ts @@ -47,10 +47,10 @@ test('Product Variant Object Path filter', async() => { objectPath: ['US', 'ValidFromDate'], filterSettings: s => s.scopes(sc => sc.fill({ apply: true }).default({ apply: false })), })) - .pagination(p => p.setPageSize(20)) + .pagination(p => p.setPageSize(3)) .build(); const result = await searcher.searchProducts(request); - expect(result?.results?.length).toBe(20); + expect(result?.results?.length).toBe(3); }); \ No newline at end of file