diff --git a/lib/src/builders/filterBuilder.ts b/lib/src/builders/filterBuilder.ts index f8b7f8d..acccfe3 100644 --- a/lib/src/builders/filterBuilder.ts +++ b/lib/src/builders/filterBuilder.ts @@ -1,6 +1,12 @@ import { AndFilter, BrandAssortmentFilter, BrandDataFilter, BrandIdFilter, CartDataFilter, ContentCategoryAssortmentFilter, ContentCategoryDataFilter, ContentCategoryHasAncestorFilter, ContentCategoryHasChildFilter, ContentCategoryHasParentFilter, ContentCategoryIdFilter, ContentCategoryLevelFilter, ContentDataFilter, ContentIdFilter, Filter, FilterCollection, OrFilter, ProductAndVariantId, ProductAndVariantIdFilter, ProductAssortmentFilter, ProductCategoryAssortmentFilter, ProductCategoryDataFilter, ProductCategoryHasAncestorFilter, ProductCategoryHasChildFilter, ProductCategoryHasParentFilter, ProductCategoryIdFilter, ProductCategoryLevelFilter, ProductDataFilter, ProductDisplayNameFilter, ProductHasVariantsFilter, ProductIdFilter, ProductListPriceFilter, ProductRecentlyPurchasedByUserFilter, ProductRecentlyViewedByUserFilter, ProductSalesPriceFilter, VariantAssortmentFilter, VariantDataFilter, VariantIdFilter, VariantListPriceFilter, VariantSalesPriceFilter, VariantSpecificationFilter } from '../models/data-contracts'; +import { FilterSettingsBuilder } from './filterSettingsBuilder'; import { ConditionBuilder } from './conditionBuilder'; +export type EntityDataFilterOptions = { + objectPath?: string[], + filterSettings?: (builder: FilterSettingsBuilder) => void +}; + export class FilterBuilder { private filters: (AndFilter | BrandAssortmentFilter @@ -463,10 +469,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addProductDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addProductDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: ProductDataFilter = { $type: 'Relewise.Client.Requests.Filters.ProductDataFilter, Relewise.Client', key: key, @@ -474,6 +483,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -488,10 +499,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addVariantDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addVariantDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: VariantDataFilter = { $type: 'Relewise.Client.Requests.Filters.VariantDataFilter, Relewise.Client', key: key, @@ -499,6 +513,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -513,10 +529,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addBrandDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addBrandDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: BrandDataFilter = { $type: 'Relewise.Client.Requests.Filters.BrandDataFilter, Relewise.Client', key: key, @@ -524,6 +543,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -563,10 +584,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addContentCategoryDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addContentCategoryDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: ContentCategoryDataFilter = { $type: 'Relewise.Client.Requests.Filters.ContentCategoryDataFilter, Relewise.Client', key: key, @@ -574,6 +598,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -588,10 +614,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addContentDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addContentDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: ContentDataFilter = { $type: 'Relewise.Client.Requests.Filters.ContentDataFilter, Relewise.Client', key: key, @@ -599,6 +628,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -613,10 +644,13 @@ export class FilterBuilder { * @param filterOutIfKeyIsNotFound * @param negated */ - public addProductCategoryDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false): this { + public addProductCategoryDataFilter(key: string, conditionBuilder: (builder: ConditionBuilder) => void, mustMatchAllConditions: boolean = true, filterOutIfKeyIsNotFound: boolean = true, negated: boolean = false, options?: EntityDataFilterOptions): this { const builder = new ConditionBuilder(); conditionBuilder(builder); + const internalSettingsBuilder = new FilterSettingsBuilder(); + options?.filterSettings?.(internalSettingsBuilder); + const filter: ProductCategoryDataFilter = { $type: 'Relewise.Client.Requests.Filters.ProductCategoryDataFilter, Relewise.Client', key: key, @@ -624,6 +658,8 @@ export class FilterBuilder { mustMatchAllConditions: mustMatchAllConditions, conditions: builder.build(), negated: negated, + objectPath: options?.objectPath, + settings: internalSettingsBuilder.build(), }; this.filters.push(filter); @@ -681,7 +717,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -697,7 +733,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -713,7 +749,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -729,7 +765,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -745,7 +781,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -761,7 +797,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -777,7 +813,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } @@ -793,7 +829,7 @@ export class FilterBuilder { negated: negated, }; this.filters.push(filter); - + return this; } diff --git a/lib/src/builders/filterScopesBuilder.ts b/lib/src/builders/filterScopesBuilder.ts new file mode 100644 index 0000000..a41a98c --- /dev/null +++ b/lib/src/builders/filterScopesBuilder.ts @@ -0,0 +1,34 @@ +import { ApplyFilterSettings, FilterScopes } from '../models/data-contracts'; + +export class FilterScopesBuilder { + + private fillScope: ApplyFilterSettings | undefined = undefined; + private defaultScope: ApplyFilterSettings | undefined = undefined; + + public fill({ apply }: { apply: boolean; }): this { + this.fillScope = { + $type: 'Relewise.Client.Requests.Filters.Settings.ApplyFilterSettings, Relewise.Client', + apply, + }; + + return this; + } + + public default({ apply }: { apply: boolean; }): this { + this.defaultScope = { + $type: 'Relewise.Client.Requests.Filters.Settings.ApplyFilterSettings, Relewise.Client', + apply, + }; + + return this; + } + + public build(): FilterScopes | null { + return this.fillScope || this.defaultScope + ? { + fill: this.fillScope, + default: this.defaultScope, + } + : null; + } +} \ No newline at end of file diff --git a/lib/src/builders/filterSettingsBuilder.ts b/lib/src/builders/filterSettingsBuilder.ts new file mode 100644 index 0000000..c8232f5 --- /dev/null +++ b/lib/src/builders/filterSettingsBuilder.ts @@ -0,0 +1,19 @@ +import { FilterSettings } from '../models/data-contracts'; +import { FilterScopesBuilder } from './filterScopesBuilder'; + +export class FilterSettingsBuilder { + private scopesBuilder: FilterScopesBuilder = new FilterScopesBuilder(); + + public scopes(builder: (builder: FilterScopesBuilder) => void): this { + builder(this.scopesBuilder); + + return this; + } + + public build(): FilterSettings | null { + const scopes = this.scopesBuilder.build(); + return scopes + ? { scopes: scopes } + : null; + } +} \ No newline at end of file diff --git a/lib/src/builders/index.ts b/lib/src/builders/index.ts index 800a494..8569d96 100644 --- a/lib/src/builders/index.ts +++ b/lib/src/builders/index.ts @@ -1,4 +1,6 @@ export * from './settings'; +export * from './filterScopesBuilder'; +export * from './filterSettingsBuilder'; export * from './dataObjectFilterConditionBuilder'; export * from './filterBuilder'; export * from './paginationBuilder'; diff --git a/lib/tests/integration-tests/filters.integration.test.ts b/lib/tests/integration-tests/filters.integration.test.ts index efe145d..ed45bbd 100644 --- a/lib/tests/integration-tests/filters.integration.test.ts +++ b/lib/tests/integration-tests/filters.integration.test.ts @@ -29,11 +29,28 @@ test('Product Assortment filter', async() => { test('Product Id filter', async() => { const request: ProductSearchRequest = baseBuilder() - .filters(f => f.addProductIdFilter(['1'])) + .filters(f => f.addProductIdFilter(['1']) + .addVariantDataFilter('avaliableMarkets', c => c.addGreaterThanCondition(1693526400 - 1))) .pagination(p => p.setPageSize(20)) .build(); const result = await searcher.searchProducts(request); expect(result?.results?.length).toBe(1); +}); + +test('Product Variant Object Path filter', async() => { + + const request: ProductSearchRequest = baseBuilder() + .filters(f => f.addVariantDataFilter('avaliableMarkets', c => c.addGreaterThanCondition(1693526400 - 1), undefined, undefined, undefined, + { + objectPath: ['US', 'ValidFromDate'], + filterSettings: s => s.scopes(sc => sc.fill({ apply: true }).default({ apply: false })), + })) + .pagination(p => p.setPageSize(20)) + .build(); + + const result = await searcher.searchProducts(request); + + expect(result?.results?.length).toBe(20); }); \ No newline at end of file diff --git a/lib/tests/integration-tests/productSearch.integration.test.ts b/lib/tests/integration-tests/productSearch.integration.test.ts index f3cf6f5..3f56459 100644 --- a/lib/tests/integration-tests/productSearch.integration.test.ts +++ b/lib/tests/integration-tests/productSearch.integration.test.ts @@ -26,14 +26,7 @@ test('ProductSearch: Relevance modifier without conditions', async() => { }); test('Product search - data object facets', async() => { - var builder = new ProductSearchBuilder({ - language: 'da', - currency: 'DKK', - displayedAtLocation: 'integration test - dataobjects', - user: UserFactory.anonymous(), - }); - - const request: ProductSearchRequest = builder + const request: ProductSearchRequest = baseProductBuilder() .facets(f => f.addProductDataObjectFacet( 't', 'Product',