Skip to content

Commit

Permalink
feat(headless SSR): fetchStaticState should only accept solutiontype-…
Browse files Browse the repository at this point in the history
…specific controllers (#4769)

This is a complex one. It introduces type changes only to ensure that
props for solution type-specific controllers are only required in their
respective engine definitions.

### Key changes:

Recommendation props are now only required in
recommendationEngineDefinition.
The same logic is applied to other solution type-specific controllers.

Additionally, unit tests have been added to verify that type inference
and conditional prop types function correctly.

https://coveord.atlassian.net/browse/KIT-3787

---------

Co-authored-by: Frederic Beaudoin <[email protected]>
  • Loading branch information
y-lakhdar and fbeaudoincoveo authored Dec 10, 2024
1 parent 96cc465 commit f961cb2
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 38 deletions.
3 changes: 2 additions & 1 deletion packages/headless-react/src/ssr-commerce/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import {
HydrateStaticStateOptions,
InferHydratedState,
InferStaticState,
NavigatorContext,
Expand Down Expand Up @@ -67,7 +68,7 @@ export function buildProviderWithDefinition(looseDefinition: LooseDefinition) {
...controllers,
...hydrateControllers,
},
})
} as HydrateStaticStateOptions<{type: string}>)
.then(({engine, controllers}) => {
setHydratedState({engine, controllers});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {defineCart} from '../../controllers/commerce/context/cart/headless-cart.ssr.js';
import {defineParameterManager} from '../../controllers/commerce/core/parameter-manager/headless-core-parameter-manager.ssr.js';
import {defineSummary} from '../../controllers/commerce/core/summary/headless-core-summary.ssr.js';
import {defineRecommendations} from '../../controllers/commerce/recommendations/headless-recommendations.ssr.js';
import {defineDidYouMean} from '../../controllers/commerce/search/did-you-mean/headless-did-you-mean.ssr.js';
import {getSampleCommerceEngineConfiguration} from './commerce-engine-configuration.js';
import {defineCommerceEngine} from './commerce-engine.ssr.js';

describe('Commerce Engine SSR', () => {
const {
listingEngineDefinition,
recommendationEngineDefinition,
searchEngineDefinition,
standaloneEngineDefinition,
} = defineCommerceEngine({
configuration: {
...getSampleCommerceEngineConfiguration(),
},
controllers: {
summary: defineSummary(),
didYouMean: defineDidYouMean(),
paramManager: defineParameterManager({listing: false}),
cart: defineCart(),
popularViewed: defineRecommendations({
options: {
slotId: 'd73afbd2-8521-4ee6-a9b8-31f064721e73',
},
}),
},
});

it('should only require cart options for standaloneEngineDefinition', () => {
expect(() =>
standaloneEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items: []}},
},
})
).not.toThrow();
});

it('should only require cart options for listingEngineDefinition', () => {
expect(() =>
listingEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items: []}},
},
})
).not.toThrow();
});

it('should only require cart and paramManager options for searchEngineDefinition', () => {
expect(() =>
searchEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items: []}},
paramManager: {initialState: {parameters: {}}},
},
})
).not.toThrow();
});

it('should only require cart options for recommendationEngineDefinition', () => {
expect(() =>
recommendationEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items: []}},
},
})
).not.toThrow();
});

it('should allow optional recommendation controller options for recommendationEngineDefinition', () => {
expect(() =>
recommendationEngineDefinition.fetchStaticState({
controllers: {
cart: {initialState: {items: []}},
popularViewed: {enabled: true, productId: 'some-product-id'},
},
})
).not.toThrow();
});
});
15 changes: 13 additions & 2 deletions packages/headless/src/app/commerce-ssr-engine/types/build.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {Controller} from '../../../controllers/controller/headless-controller.js';
import type {
ControllersMap,
ControllersPropsMap,
Expand All @@ -6,7 +7,11 @@ import type {
OptionsTuple,
} from '../../ssr-engine/types/common.js';
import {SSRCommerceEngine} from '../factories/build-factory.js';
import {EngineDefinitionControllersPropsOption} from './common.js';
import {
ControllerDefinitionsMap,
EngineDefinitionControllersPropsOption,
SolutionType,
} from './common.js';

export interface BuildOptions<TEngineOptions> {
extend?: OptionsExtender<TEngineOptions>;
Expand All @@ -16,14 +21,20 @@ export type Build<
TEngineOptions,
TControllersMap extends ControllersMap,
TControllersProps extends ControllersPropsMap,
TControllersDefinitionsMap extends ControllerDefinitionsMap<Controller>,
TSolutionType extends SolutionType,
> = {
/**
* Initializes an engine and controllers from the definition.
*/
(
...params: OptionsTuple<
BuildOptions<TEngineOptions> &
EngineDefinitionControllersPropsOption<TControllersProps>
EngineDefinitionControllersPropsOption<
TControllersDefinitionsMap,
TControllersProps,
TSolutionType
>
>
): Promise<EngineDefinitionBuildResult<SSRCommerceEngine, TControllersMap>>;
};
Loading

0 comments on commit f961cb2

Please sign in to comment.