Skip to content

Commit

Permalink
fix: commerce pages in ssr
Browse files Browse the repository at this point in the history
  • Loading branch information
richard190m committed Jan 9, 2024
1 parent 8048bc6 commit ca1a08e
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
mockCommercePagesInitialState,
mockCommercePagesLoadingState,
mockCommercePagesState,
slug,
} from 'tests/__fixtures__/contents/index.mjs';
import { fetchCommercePages } from '@farfetch/blackout-redux';
import { withStore } from '../../../../tests/helpers/index.js';
Expand Down Expand Up @@ -84,6 +85,7 @@ describe('useCommercePages', () => {

expect(fetchCommercePages).toHaveBeenCalledWith(
{
slug,
brand: commercePageQuery.brand,
category: commercePageQuery.category,
contentTypeCode: commercePageQuery.contentTypeCode,
Expand Down Expand Up @@ -124,6 +126,7 @@ describe('useCommercePages', () => {

expect(fetchCommercePages).toHaveBeenCalledWith(
{
slug,
brand: commercePageQuery.brand,
category: commercePageQuery.category,
contentTypeCode: commercePageQuery.contentTypeCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cleanup, renderHook } from '@testing-library/react';
import { ContentPageType } from '@farfetch/blackout-client';
import { fetchContentPage } from '@farfetch/blackout-redux';
import {
mockCommercePageWithDataState,
mockContentPageEntry,
mockContentPageErrorState,
mockContentPageInitialState,
Expand Down Expand Up @@ -102,6 +103,34 @@ describe('useContentPage', () => {

expect(fetchContentPage).not.toHaveBeenCalled();
});

it('should return values correctly with commercePagesHash option', () => {
const { result } = renderHook(
() =>
useContentPage(
ContentPageType.Listing,
{ slug },
{ isCommercePage: true },
),
{
wrapper: withStore(mockCommercePageWithDataState),
},
);

expect(result.current).toStrictEqual({
data: [
mockCommercePageWithDataState.entities.contents[
mockContentPageEntry.publicationId
],
],
isLoading: false,
error: null,
isFetched: true,
actions: {
fetch: expect.any(Function),
},
});
});
});

describe('actions', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { CommercePagesRankingStrategy } from '@farfetch/blackout-redux';
import type { Config, QueryCommercePages } from '@farfetch/blackout-client';

export interface UseCommercePagesOptions extends QueryCommercePages {
slug: string;
enableAutoFetch?: boolean;
strategy?: CommercePagesRankingStrategy;
fetchConfig?: Config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import type { Config } from '@farfetch/blackout-client';
export interface UseContentPageOptions {
enableAutoFetch?: boolean;
fetchConfig?: Config;
isCommercePage?: boolean;
}
2 changes: 1 addition & 1 deletion packages/react/src/contents/hooks/useCommercePages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const useCommercePages = <T = ComponentType[]>(
const query = useMemo(
() => ({
contentTypeCode: ContentTypeCode.CommercePages,
...fetchQuery,
codes: fetchQuery.slug,
}),
[fetchQuery],
);
Expand Down
14 changes: 10 additions & 4 deletions packages/react/src/contents/hooks/useContentPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,20 @@ const useContentPage = <T = [ComponentType]>(
) => {
const store = useStore();

const { enableAutoFetch = true, fetchConfig } = options;
const {
enableAutoFetch = true,
fetchConfig,
isCommercePage = false,
} = options;

const query = useMemo(
() => ({
contentTypeCode: ContentTypeCode.ContentPage,
codes: fetchQuery.slug.split('?')[0] as string,
contentTypeCode: isCommercePage
? ContentTypeCode.CommercePages
: ContentTypeCode.ContentPage,
codes: fetchQuery.slug,
}),
[fetchQuery.slug],
[fetchQuery.slug, isCommercePage],
);

const fetchQueryWithoutSlug = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
type CommercePages,
type Config,
type GetCommercePages,
type QueryCommercePages,
toBlackoutError,
} from '@farfetch/blackout-client';
import {
type CommercePagesRankingStrategy,
ContentTypeCode,
type FetchCommercePagesAction,
type QueryCommercePagesWithSlug,
} from '../../types/index.js';
import { contentEntries } from '../../../entities/schemas/content.js';
import { normalize } from 'normalizr';
Expand All @@ -29,7 +29,7 @@ import type { Dispatch } from 'redux';
const fetchCommercePagesFactory =
(getCommercePages: GetCommercePages) =>
(
query: QueryCommercePages,
query: QueryCommercePagesWithSlug,
strategy?: CommercePagesRankingStrategy,
config?: Config,
) =>
Expand All @@ -39,17 +39,19 @@ const fetchCommercePagesFactory =
let hash: string | undefined;

try {
const { slug, ...queryWithoutSlug } = query;

hash = generateContentHash({
contentTypeCode: ContentTypeCode.CommercePages,
...query,
codes: slug,
});

dispatch({
payload: { hash },
type: actionTypes.FETCH_COMMERCE_PAGES_REQUEST,
});

const result = await getCommercePages(query, config);
const result = await getCommercePages(queryWithoutSlug, config);
const rankedResult = applyCommercePagesRankingStrategy(result, strategy);

dispatch({
Expand Down
33 changes: 21 additions & 12 deletions packages/redux/src/contents/serverInitialState.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {
applyCommercePagesRankingStrategy,
generateContentHash,
} from './utils.js';
import { buildQueryStringFromObject } from '../helpers/index.js';
import { contentEntries } from '../entities/schemas/content.js';
import { generateContentHash } from './utils.js';
import { type ContentsState } from './types/index.js';
import { get, merge } from 'lodash-es';
import { INITIAL_STATE_CONTENT } from './reducer.js';
import { normalize } from 'normalizr';
import parse from 'url-parse';
import type { ContentsState } from './types/index.js';
import type { ServerInitialState } from '../types/serverInitialState.types.js';

/**
Expand All @@ -15,28 +18,35 @@ import type { ServerInitialState } from '../types/serverInitialState.types.js';
*
* @returns Initial state for the contents reducer.
*/
const serverInitialState: ServerInitialState = ({ model }) => {
const serverInitialState: ServerInitialState = ({ model, strategy }) => {
if (!get(model, 'searchContentRequests')) {
return { contents: INITIAL_STATE_CONTENT };
}

const { searchContentRequests, slug, seoMetadata, subfolder } = model;
const url = subfolder !== '/' ? slug?.replace(subfolder, '') : slug;

const contents = searchContentRequests.reduce((acc, item) => {
const { searchResponse } = item;
const firstSearchResponseItem = searchResponse.entries[0];
const {
searchResponse,
filters: { codes, contentTypeCode },
} = item;
let response = searchResponse;
const isCommercePage = contentTypeCode === 'commerce_pages';

if (!firstSearchResponseItem) {
return acc;
}
const code = isCommercePage ? url : codes?.[0];

const hash = generateContentHash({
codes: firstSearchResponseItem.code,
contentTypeCode: firstSearchResponseItem.contentTypeCode,
codes: code,
contentTypeCode: contentTypeCode,
});

if (isCommercePage) {
response = applyCommercePagesRankingStrategy(searchResponse, strategy);
}

const { entities, result } = {
...normalize({ hash, ...searchResponse }, contentEntries),
...normalize({ hash, ...response }, contentEntries),
};

return merge(acc, {
Expand All @@ -53,7 +63,6 @@ const serverInitialState: ServerInitialState = ({ model }) => {
});
}, {});

const url = subfolder !== '/' ? slug?.replace(subfolder, '') : slug;
const { pathname, query } = parse(url, true);

delete query.json;
Expand Down
5 changes: 5 additions & 0 deletions packages/redux/src/contents/types/actions.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
BlackoutError,
Contents,
ContentType,
QueryCommercePages,
SEOMetadata,
} from '@farfetch/blackout-client';
import type { ContentEntity } from '../../entities/index.js';
Expand All @@ -22,6 +23,10 @@ export type ContentsPayload = NormalizedSchema<
ContentsNormalized
> & { hash: Hash };

export interface QueryCommercePagesWithSlug extends QueryCommercePages {
slug: string;
}

/**
* Fetch Content Page Action
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/redux/src/types/serverInitialState.types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { CommercePagesRankingStrategy } from '../contents/types/commercePagesRankingStrategy.types.js';
import type { LocaleState } from '../locale/index.js';
import type { Model } from './model.types.js';
import type { ProductsState } from '../products/index.js';
import type { StoreState } from './storeState.types.js';

export type ServerInitialState = (data: {
model: Model;
strategy?: CommercePagesRankingStrategy;
options?: { productImgQueryParam?: string };
}) => Omit<StoreState, 'products' | 'locale'> & {
products?: Partial<ProductsState>;
Expand Down
6 changes: 5 additions & 1 deletion tests/__fixtures__/contents/commercePages.fixtures.mts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const commercePagesQuery = {

export const slug = 'woman/gucci';
export const commercePageQuery = {
slug,
brand: 5030844,
category: '136643',
type: CommercePagesType.Listing,
Expand All @@ -28,7 +29,10 @@ export const commercePageQuery = {
export const commercePageContentPublicationId =
'dc9c0c95-9485-45c2-a76c-6923bb39b544';

export const commercePagesHash = generateContentHash(commercePageQuery);
export const commercePagesHash = generateContentHash({
contentTypeCode: commercePageQuery.contentTypeCode,
codes: commercePageQuery.slug,
});

export const mockCommercePages = {
number: 1,
Expand Down
43 changes: 43 additions & 0 deletions tests/__fixtures__/contents/contentPage.fixtures.mts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ const contentQuery = {

export const contentPagesHash = generateContentHash(contentQuery);

const commerceQuery = {
codes: slugContentWithoutQuery,
contentTypeCode: 'commerce_pages',
};
const commercePagesHash = generateContentHash(commerceQuery);

export const mockContentPageEntry = {
publicationId: 'dc9c0c95-9485-45c2-a76c-6923bb39b544',
versionId: '78f1922d-0ef1-46ed-b02c-ca541d0a0d80',
Expand Down Expand Up @@ -65,6 +71,11 @@ export const mockContentPageEntry = {
],
};

const mockCommercePageEntry = {
...mockContentPageEntry,
contentTypeCode: 'commerce_pages',
};

export const mockContentPage = {
number: 1,
totalItems: 1,
Expand Down Expand Up @@ -153,3 +164,35 @@ export const mockContentPageWithDataState = {
},
},
};

export const mockCommercePageWithDataState = {
entities: {
contents: {
[mockCommercePageEntry.publicationId]: {
...mockCommercePageEntry,
publicationDate: 0,
metadata: {
...mockCommercePageEntry.metadata,
custom: {
...mockCommercePageEntry.metadata.custom,
eventDate: 0,
},
},
},
},
},
contents: {
...mockContentPageInitialState.contents,
searchResults: {
[commercePagesHash]: {
isLoading: false,
error: null,
result: {
hash: commercePagesHash,
...mockContentPage,
entries: [mockCommercePageEntry.publicationId],
},
},
},
},
};

0 comments on commit ca1a08e

Please sign in to comment.