Skip to content

Commit

Permalink
refactor(client-recommend): small refactors to recommendation filters
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisFrazier77 committed Oct 18, 2023
1 parent 5c01915 commit 7f50c46
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 103 deletions.
29 changes: 29 additions & 0 deletions packages/snap-client/src/Client/apis/Recommend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,35 @@ describe('Recommend Api', () => {
requestMock.mockReset();
});

it('batchRecommendations handles filters expected', async () => {
const api = new RecommendAPI(new ApiConfiguration({}));

const requestMock = jest
.spyOn(global.window, 'fetch')
.mockImplementation(() => Promise.resolve({ status: 200, json: () => Promise.resolve(mockData.recommend()) } as Response));

// @ts-ignore
api.batchRecommendations({
tags: ['crossSell'],
limits: 10,
filters: [
{
type: 'value',
field: 'color',
value: 'red',
},
],
...batchParams,
});

//add delay for paramBatch.timeout
await wait(250);
const reorderedGetURL =
'https://8uyt2m.a.searchspring.io/boost/8uyt2m/recommend?tags=crossSell&limits=10&siteId=8uyt2m&lastViewed=marnie-runner-2-7x10&lastViewed=ruby-runner-2-7x10&lastViewed=abbie-runner-2-7x10&lastViewed=riley-4x6&lastViewed=joely-5x8&lastViewed=helena-4x6&lastViewed=kwame-4x6&lastViewed=sadie-4x6&lastViewed=candice-runner-2-7x10&lastViewed=esmeray-4x6&lastViewed=camilla-230x160&lastViewed=candice-4x6&lastViewed=sahara-4x6&lastViewed=dayna-4x6&lastViewed=moema-4x6&product=marnie-runner-2-7x10&filter.color=red';
expect(requestMock).toHaveBeenCalledWith(reorderedGetURL, GETParams);
requestMock.mockReset();
});

it('batchRecommendations resolves in right order with order prop', async () => {
const api = new RecommendAPI(new ApiConfiguration({}));
const response = mockData.file('recommend/results/8uyt2m/ordered.json');
Expand Down
13 changes: 6 additions & 7 deletions packages/snap-client/src/Client/apis/Recommend.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { API, ApiConfiguration } from './Abstract';
import { HTTPHeaders, filtersObj, PostRecommendationRequestFiltersModel, GetRecommendRequestModel } from '../../types';
import { HTTPHeaders, PostRecommendRequestFiltersModel, PostRecommendRequestModel, GetRecommendRequestModel } from '../../types';
import { AppMode, charsParams } from '@searchspring/snap-toolbox';
import { transformRecommendationFiltersGet, transformRecommendationFiltersPost } from '../transforms';
import { ProfileRequestModel, ProfileResponseModel, RecommendRequestModel, RecommendResponseModel } from '../../types';
Expand Down Expand Up @@ -106,20 +106,19 @@ export class RecommendAPI extends API {
}

let response: RecommendResponseModel;

if (charsParams(batch.request) > 1024) {
if (batch.request['product']) {
batch.request['product'] = batch.request['product'].toString();
}

// //transform filters here
//transform filters here
if (batch.request.filters) {
(batch.request as PostRecommendationRequestFiltersModel)['filters'] = transformRecommendationFiltersPost(
(batch.request as PostRecommendRequestModel)['filters'] = transformRecommendationFiltersPost(
batch.request.filters
) as filtersObj[];
) as PostRecommendRequestFiltersModel[];
}

response = await this.postRecommendations(batch.request as PostRecommendationRequestFiltersModel);
response = await this.postRecommendations(batch.request as PostRecommendRequestModel);
} else {
if (batch.request.filters) {
const filters = transformRecommendationFiltersGet(batch.request.filters);
Expand Down Expand Up @@ -167,7 +166,7 @@ export class RecommendAPI extends API {
return response as unknown as RecommendResponseModel;
}

async postRecommendations(requestParameters: PostRecommendationRequestFiltersModel): Promise<RecommendResponseModel> {
async postRecommendations(requestParameters: PostRecommendRequestModel): Promise<RecommendResponseModel> {
const headerParameters: HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { transformRecommendationFiltersGet } from './recommendationFiltersGet';

it('transformFilters works with GET', async () => {
const filters = [
{ type: 'value' as const, field: 'color', value: 'blue' },
{ type: 'value' as const, field: 'color', value: 'red' },
{ type: 'value' as const, field: 'color', value: 'green' },
{ type: 'range' as const, field: 'price', value: { high: 20, low: 0 } },
];
const tranformedFilters = transformRecommendationFiltersGet(filters);
expect(tranformedFilters).toStrictEqual({ 'filter.color': ['blue', 'red', 'green'], 'filter.price.high': [20], 'filter.price.low': [0] });
});
describe('transformRecommendationFiltersGet', () => {
it('transformRecommendationFiltersGet works', async () => {
const filters = [
{ type: 'value' as const, field: 'color', value: 'blue' },
{ type: 'value' as const, field: 'color', value: 'red' },
{ type: 'value' as const, field: 'color', value: 'green' },
{ type: 'range' as const, field: 'price', value: { high: 20, low: 0 } },
];
const tranformedFilters = transformRecommendationFiltersGet(filters);
expect(tranformedFilters).toStrictEqual({ 'filter.color': ['blue', 'red', 'green'], 'filter.price.high': [20], 'filter.price.low': [0] });
});

it('transformFilters GET works with single high or low range value', async () => {
const filterHigh = [{ type: 'range' as const, field: 'price', value: { high: 20 } }];
const filterLow = [
//also works with 0
{ type: 'range' as const, field: 'price', value: { low: 0 } },
];
it('transformRecommendationFiltersGet works with single high or low range value', async () => {
const filterHigh = [{ type: 'range' as const, field: 'price', value: { high: 20 } }];
const filterLow = [
//also works with 0
{ type: 'range' as const, field: 'price', value: { low: 0 } },
];

const tranformedFilterHigh = transformRecommendationFiltersGet(filterHigh);
expect(tranformedFilterHigh).toStrictEqual({ 'filter.price.high': [20] });
const tranformedFilterHigh = transformRecommendationFiltersGet(filterHigh);
expect(tranformedFilterHigh).toStrictEqual({ 'filter.price.high': [20] });

const tranformedFilterLow = transformRecommendationFiltersGet(filterLow);
expect(tranformedFilterLow).toStrictEqual({ 'filter.price.low': [0] });
const tranformedFilterLow = transformRecommendationFiltersGet(filterLow);
expect(tranformedFilterLow).toStrictEqual({ 'filter.price.low': [0] });
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const transformRecommendationFiltersGet = (filters: RecommendationRequest
if (filter.type == 'value') {
//check if filterArray contains a filter for this value already
if (filterArray[`filter.${filter.field}`]) {
// is the existing filter an array already? or just a single value
filterArray[`filter.${filter.field}`].push(filter.value);
} else {
//else create a new one
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import { transformRecommendationFiltersPost } from './recommendationFiltersPost';

it('transformFilters works with POST', async () => {
const filters = [
{ type: 'value' as const, field: 'color', value: 'blue' },
{ type: 'value' as const, field: 'color', value: 'red' },
{ type: 'value' as const, field: 'color', value: 'green' },
{ type: 'range' as const, field: 'price', value: { high: 20, low: 0 } },
];
describe('transformRecommendationFiltersPost', () => {
it('transformRecommendationFiltersPost works as expected', async () => {
const filters = [
{ type: 'value' as const, field: 'color', value: 'blue' },
{ type: 'value' as const, field: 'color', value: 'red' },
{ type: 'value' as const, field: 'color', value: 'green' },
{ type: 'range' as const, field: 'price', value: { high: 20, low: 0 } },
];

const tranformedFilters = transformRecommendationFiltersPost(filters);
expect(tranformedFilters).toStrictEqual([
{ field: 'color', type: '=', values: ['blue', 'red', 'green'] },
{ field: 'price', type: '>=', values: [0] },
{ field: 'price', type: '<=', values: [20] },
]);
});
const tranformedFilters = transformRecommendationFiltersPost(filters);
expect(tranformedFilters).toStrictEqual([
{ field: 'color', type: '=', values: ['blue', 'red', 'green'] },
{ field: 'price', type: '>=', values: [0] },
{ field: 'price', type: '<=', values: [20] },
]);
});

it('transformFilters POST works with single high or low range value', async () => {
const filterHigh = [{ type: 'range' as const, field: 'price', value: { high: 20 } }];
const filterLow = [{ type: 'range' as const, field: 'price', value: { low: 0 } }];
it('transformRecommendationFiltersPost works with single high or low range value', async () => {
const filterHigh = [{ type: 'range' as const, field: 'price', value: { high: 20 } }];
const filterLow = [{ type: 'range' as const, field: 'price', value: { low: 0 } }];

const tranformedFilterHigh = transformRecommendationFiltersPost(filterHigh);
expect(tranformedFilterHigh).toStrictEqual([{ field: 'price', type: '<=', values: [20] }]);
const tranformedFilterHigh = transformRecommendationFiltersPost(filterHigh);
expect(tranformedFilterHigh).toStrictEqual([{ field: 'price', type: '<=', values: [20] }]);

const tranformedFilterLow = transformRecommendationFiltersPost(filterLow);
expect(tranformedFilterLow).toStrictEqual([{ field: 'price', type: '>=', values: [0] }]);
const tranformedFilterLow = transformRecommendationFiltersPost(filterLow);
expect(tranformedFilterLow).toStrictEqual([{ field: 'price', type: '>=', values: [0] }]);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { RecommendationRequestFilterModel, filtersObj } from '../../types';
import { RecommendationRequestFilterModel, PostRecommendRequestFiltersModel } from '../../types';

export const transformRecommendationFiltersPost = (filters: RecommendationRequestFilterModel[]) => {
const filterArray: filtersObj[] = [];
const filterArray: PostRecommendRequestFiltersModel[] = [];
filters.map((filter) => {
if (filter.type == 'value') {
//check if filterArray contains a filter for this value already
Expand All @@ -27,7 +27,14 @@ export const transformRecommendationFiltersPost = (filters: RecommendationReques
type: '>=' as const,
values: [filter.value.low as number],
};
filterArray.push(low);

//dedupe
const i = filterArray.findIndex((_filter) => _filter.field == filter.field && _filter.type == '>=');
if (i > -1) {
filterArray[i] = low;
} else {
filterArray.push(low);
}
}
//high
if (typeof filter.value.high == 'number') {
Expand All @@ -36,7 +43,14 @@ export const transformRecommendationFiltersPost = (filters: RecommendationReques
type: '<=' as const,
values: [filter.value.high as number],
};
filterArray.push(high);

//dedupe
const i = filterArray.findIndex((_filter) => _filter.field == filter.field && _filter.type == '<=');
if (i > -1) {
filterArray[i] = high;
} else {
filterArray.push(high);
}
}
}
});
Expand Down
6 changes: 3 additions & 3 deletions packages/snap-client/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ export type GetRecommendRequestModel = Omit<RecommendRequestModel, 'filters'> &
[filter: `filter.${string}`]: (string | number)[];
};

export type PostRecommendationRequestFiltersModel = Omit<RecommendRequestModel, 'filters'> & {
filters?: filtersObj[];
export type PostRecommendRequestModel = Omit<RecommendRequestModel, 'filters'> & {
filters?: PostRecommendRequestFiltersModel[];
};

export type filtersObj = {
export type PostRecommendRequestFiltersModel = {
field: string;
type: '=' | '==' | '===' | '!=' | '!==' | '>' | '<' | '>=' | '<=';
values: (string | number)[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,6 @@ export class RecommendationController extends AbstractController {
const cart = this.tracker.cookies.cart.get();
const lastViewed = this.tracker.cookies.viewed.get();

if (this.context.options?.filters) {
params.filters = this.context.options?.filters;
}

if (shopperId) {
params.shopper = shopperId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,45 +133,5 @@ describe('Recommendations', () => {
});
});
});

it('adds filters to store', function () {
const filters = [
{
type: 'value',
field: 'color',
value: 'blue',
},
{
type: 'range',
field: 'price',
value: { low: 0, high: 20 },
},
];
cy.on('window:before:load', (win) => {
win.mergeSnapConfig = {
instantiators: {
recommendation: {
config: {
globals: {
filters: filters,
},
},
},
},
};
});

cy.visit(config.url);

it('snap bundle exists on product page', () => {
cy.waitForBundle().then((searchspring) => {
expect(searchspring).to.exist;
});
});

cy.snapController(config?.selectors?.recommendation.controller).then(({ store }) => {
expect(store.config.globals.filters).to.deep.equal(filters);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,21 @@ describe('RecommendationInstantiator', () => {
branch: 'testing',
siteId: 'abc123',
categories: ['cats', 'dogs'],
limit: 5
limit: 5,
filters: [
{
type: 'value',
field: 'color',
value: 'blue'
},
{
type: 'range',
field: 'price',
value: { low: 0, high: 20 }
}
]
}
</script>`;

const client = new MockClient(baseConfig.client!.globals, {});
Expand All @@ -305,6 +318,18 @@ describe('RecommendationInstantiator', () => {
options: {
branch: 'testing',
categories: ['cats', 'dogs'],
filters: [
{
type: 'value',
field: 'color',
value: 'blue',
},
{
type: 'range',
field: 'price',
value: { low: 0, high: 20 },
},
],
limit: 5,
siteId: 'abc123',
},
Expand All @@ -316,6 +341,18 @@ describe('RecommendationInstantiator', () => {
batched: true,
branch: 'testing',
categories: ['cats', 'dogs'],
filters: [
{
type: 'value',
field: 'color',
value: 'blue',
},
{
type: 'range',
field: 'price',
value: { low: 0, high: 20 },
},
],
limits: 5,
product: 'sku1',
shopper: 'snapdev',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export class RecommendationInstantiator {
if (options?.categories) {
contextGlobals.categories = options.categories;
}
if (options?.filters) {
contextGlobals.filters = options.filters;
}
if (options?.limit && Number.isInteger(Number(options?.limit))) {
contextGlobals.limits = Number(options?.limit);
}
Expand Down Expand Up @@ -193,8 +196,8 @@ export class RecommendationInstantiator {
limits: 20,
};
const globals = deepmerge(
deepmerge(deepmerge(defaultGlobals, this.config.client?.globals || {}), contextGlobals),
(this.config.config?.globals as any) || {}
deepmerge(deepmerge(defaultGlobals, this.config.client?.globals || {}), (this.config.config?.globals as any) || {}),
contextGlobals
);

const controllerConfig = {
Expand Down

0 comments on commit 7f50c46

Please sign in to comment.