Skip to content

Commit

Permalink
only 2 errors
Browse files Browse the repository at this point in the history
  • Loading branch information
alexprudhomme committed Nov 14, 2024
1 parent 82e8cb1 commit f2184e6
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 73 deletions.
190 changes: 151 additions & 39 deletions packages/headless/src/app/commerce-engine/commerce-engine.ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import {
EngineDefinition,
EngineDefinitionOptions,
RecommendationEngineDefinition,
} from '../commerce-ssr-engine/types/core-engine.js';
import {buildLogger} from '../logger.js';
import {NavigatorContextProvider} from '../navigatorContextProvider.js';
Expand Down Expand Up @@ -137,6 +138,14 @@ function buildSSRCommerceEngine(
};
}

interface RecommendationCommerceEngineDefinition<
TControllers extends ControllerDefinitionsMap<SSRCommerceEngine, Controller>,
> extends RecommendationEngineDefinition<
SSRCommerceEngine,
TControllers,
CommerceEngineOptions
> {}

export interface CommerceEngineDefinition<
TControllers extends ControllerDefinitionsMap<SSRCommerceEngine, Controller>,
TSolutionType extends SolutionType,
Expand Down Expand Up @@ -173,10 +182,7 @@ export function defineCommerceEngine<
TControllerDefinitions,
SolutionType.standalone
>;
recommendationEngineDefinition: CommerceEngineDefinition<
TControllerDefinitions,
SolutionType.recommendation
>;
recommendationEngineDefinition: RecommendationCommerceEngineDefinition<TControllerDefinitions>;
} {
const {controllers: controllerDefinitions, ...engineOptions} = options;
type Definition = CommerceEngineDefinition<
Expand All @@ -199,6 +205,22 @@ export function defineCommerceEngine<
type HydrateStaticStateFromBuildResultParameters =
Parameters<HydrateStaticStateFromBuildResultFunction>;

type RecommendationDefinition =
RecommendationCommerceEngineDefinition<TControllerDefinitions>;
type RecommendationBuildFunction = RecommendationDefinition['build'];
type RecommendationFetchStaticStateFunction =
RecommendationDefinition['fetchStaticState'];
type RecommendationHydrateStaticStateFunction =
RecommendationDefinition['hydrateStaticState'];
type RecommendationFetchStaticStateFromBuildResultFunction =
RecommendationFetchStaticStateFunction['fromBuildResult'];
type RecommendationHydrateStaticStateFromBuildResultFunction =
RecommendationHydrateStaticStateFunction['fromBuildResult'];
type RecommendationBuildParameters = Parameters<RecommendationBuildFunction>;
type RecommendationFetchStaticStateParameters =
Parameters<RecommendationFetchStaticStateFunction>;
type RecommendationFetchStaticFromBuildResultsParameters =
Parameters<RecommendationFetchStaticStateFromBuildResultFunction>;
const recommendationFilter = buildRecommendationFilter(
controllerDefinitions ?? {}
);
Expand All @@ -222,6 +244,11 @@ export function defineCommerceEngine<
'[WARNING] Missing navigator context in server-side code. Make sure to set it with `setNavigatorContextProvider` before calling fetchStaticState()'
);
}

// These are the recs..
// But why do I even need it in the build factory ?????
// logger.warn(buildOptions?.c);

const engine = buildSSRCommerceEngine(
solutionType,
buildOptions?.extend
Expand Down Expand Up @@ -249,6 +276,7 @@ export function defineCommerceEngine<
) => FetchStaticStateFunction = (solutionType: SolutionType) =>
composeFunction(
async (...params: FetchStaticStateParameters) => {
// I can't do it all, I need to split them all
const buildResult = await buildFactory(solutionType)(...params);
const staticState = await fetchStaticStateFactory(
solutionType
Expand Down Expand Up @@ -327,6 +355,93 @@ export function defineCommerceEngine<
},
}
);

const recommendationBuildFactory =
() =>
async (...[buildOptions]: RecommendationBuildParameters) => {
const logger = buildLogger(options.loggerOptions);
if (!getOptions().navigatorContextProvider) {
logger.warn(
'[WARNING] Missing navigator context in server-side code. Make sure to set it with `setNavigatorContextProvider` before calling fetchStaticState()'
);
}

// These are the recs..
// But why do I even need it in the build factory ?????
// logger.warn(buildOptions?.c);

const engine = buildSSRCommerceEngine(
SolutionType.recommendation,
buildOptions?.extend
? await buildOptions.extend(getOptions())
: getOptions(),
recommendationFilter.count
);
const controllers = buildControllerDefinitions({
definitionsMap: (controllerDefinitions ?? {}) as TControllerDefinitions,
engine,
solutionType: SolutionType.recommendation,
propsMap: (buildOptions && 'controllers' in buildOptions
? buildOptions.controllers
: {}) as InferControllerPropsMapFromDefinitions<TControllerDefinitions>,
});

return {
engine,
controllers,
};
};

const recommendationFetchStaticStateFactory: () => RecommendationFetchStaticStateFunction =
(solutionType: SolutionType) =>
composeFunction(
async (...params: RecommendationFetchStaticStateParameters) => {
const buildResult = await recommendationBuildFactory()(...params);
// I can't do it all, I need to split them all
//What the hell, this function calls itself ?
const staticState =
await recommendationFetchStaticStateFactory().fromBuildResult({
buildResult,
});
return staticState;
},
{
fromBuildResult: async (
...params: RecommendationFetchStaticFromBuildResultsParameters
) => {
const [
{
buildResult: {engine, controllers},
},
] = params;

if (solutionType === SolutionType.listing) {
buildProductListing(engine).executeFirstRequest();
} else if (solutionType === SolutionType.search) {
buildSearch(engine).executeFirstSearch();
} else if (solutionType === SolutionType.recommendation) {
// here build the filter and refresh them all
// build every recommendation and refresh them all ?
// buildRecommendations(engine).refresh();
recommendationFilter.refresh(controllers);
}

const searchAction = await engine.waitForRequestCompletedAction();

return createStaticState({
searchAction,
controllers,
}) as EngineStaticState<
UnknownAction,
InferControllerStaticStateMapFromDefinitionsWithSolutionType<
TControllerDefinitions,
SolutionType.recommendation
>
>;
},
}
);

return {
listingEngineDefinition: {
build: buildFactory(SolutionType.listing),
Expand All @@ -350,45 +465,42 @@ export function defineCommerceEngine<
SolutionType.standalone
>,
recommendationEngineDefinition: {
build: buildFactory(SolutionType.recommendation),
fetchStaticState: fetchStaticStateFactory(SolutionType.recommendation),
build: recommendationBuildFactory(),
fetchStaticState: recommendationFetchStaticStateFactory(),
hydrateStaticState: hydrateStaticStateFactory(
SolutionType.recommendation
),
setNavigatorContextProvider,
} as CommerceEngineDefinition<
TControllerDefinitions,
SolutionType.recommendation
>,
} as RecommendationCommerceEngineDefinition<TControllerDefinitions>,
};
}
/// Sandbox
// const {
// recommendationEngineDefinition,
// searchEngineDefinition,
// standaloneEngineDefinition,
// } = defineCommerceEngine({
// configuration: getSampleCommerceEngineConfiguration(),
// controllers: {
// standaloneSearchBox: defineStandaloneSearchBox({
// options: {redirectionUrl: 'rest'},
// }),
// facets: defineFacetGenerator(),
// trending: defineRecommendations({
// options: {slotId: 'ttt'},
// }),
// popular: defineRecommendations({
// options: {slotId: 'ppp'},
// }),
// },
// });

// // TODO: should have a way to select which recommendation to fetch
// const r = await standaloneEngineDefinition.fetchStaticState();
// r.controllers.standaloneSearchBox;

// const b = await recommendationEngineDefinition.fetchStaticState(['trending']);
// b.controllers.trending;

// const a = await searchEngineDefinition.fetchStaticState();
// a.controllers; // TODO: should throw an error since it's not defined in search
const {
recommendationEngineDefinition,
searchEngineDefinition,
standaloneEngineDefinition,
} = defineCommerceEngine({
configuration: getSampleCommerceEngineConfiguration(),
controllers: {
standaloneSearchBox: defineStandaloneSearchBox({
options: {redirectionUrl: 'rest'},
}),
facets: defineFacetGenerator(),
trending: defineRecommendations({
options: {slotId: 'ttt'},
}),
popular: defineRecommendations({
options: {slotId: 'ppp'},
}),
},
});

// TODO: should have a way to select which recommendation to fetch
const r = await standaloneEngineDefinition.fetchStaticState();
r.controllers.standaloneSearchBox;

const b = await recommendationEngineDefinition.fetchStaticState(['popular']);
b.controllers.trending;

const a = await searchEngineDefinition.fetchStaticState();
a.controllers; // TODO: should throw an error since it's not defined in search
108 changes: 74 additions & 34 deletions packages/headless/src/app/commerce-ssr-engine/types/core-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,16 @@ export interface EngineDefinition<
/**
* Fetches the static state on the server side using your engine definition.
*/
fetchStaticState: TSolutionType extends SolutionType.recommendation
? FetchStaticStateWithList<
TEngine,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
UnknownAction,
InferControllerStaticStateMapFromDefinitionsWithSolutionType<
TControllers,
TSolutionType
>,
InferControllerPropsMapFromDefinitions<TControllers>
>
: FetchStaticState<
TEngine,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
UnknownAction,
InferControllerStaticStateMapFromDefinitionsWithSolutionType<
TControllers,
TSolutionType
>,
InferControllerPropsMapFromDefinitions<TControllers>
>;
fetchStaticState: FetchStaticState<
TEngine,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
UnknownAction,
InferControllerStaticStateMapFromDefinitionsWithSolutionType<
TControllers,
TSolutionType
>,
InferControllerPropsMapFromDefinitions<TControllers>
>;
/**
* Fetches the hydrated state on the client side using your engine definition and the static state.
*/
Expand All @@ -73,19 +62,70 @@ export interface EngineDefinition<
/**
* Builds an engine and its controllers from an engine definition.
*/
build: TSolutionType extends SolutionType.recommendation
? BuildWithList<
TEngine,
TEngineOptions,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
InferControllerPropsMapFromDefinitions<TControllers>
>
: Build<
TEngine,
TEngineOptions,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
InferControllerPropsMapFromDefinitions<TControllers>
>;
build: Build<
TEngine,
TEngineOptions,
InferControllersMapFromDefinition<TControllers, TSolutionType>,
InferControllerPropsMapFromDefinitions<TControllers>
>;

/**
* Sets the navigator context provider.
* This provider is essential for retrieving navigation-related data such as referrer, userAgent, location, and clientId, which are crucial for handling both server-side and client-side API requests effectively.
*
* Note: The implementation specifics of the navigator context provider depend on the Node.js framework being utilized. It is the developer's responsibility to appropriately define and implement the navigator context provider to ensure accurate navigation context is available throughout the application. If the user fails to provide a navigator context provider, a warning will be logged either on the server or the browser console.
*/
setNavigatorContextProvider: (
navigatorContextProvider: NavigatorContextProvider
) => void;
}

export interface RecommendationEngineDefinition<
TEngine extends CoreEngine | CoreEngineNext,
TControllers extends ControllerDefinitionsMap<TEngine, Controller>,
TEngineOptions,
> {
/**
* Fetches the static state on the server side using your engine definition.
*/
fetchStaticState: FetchStaticStateWithList<
TEngine,
InferControllersMapFromDefinition<
TControllers,
SolutionType.recommendation
>,
UnknownAction,
InferControllerStaticStateMapFromDefinitionsWithSolutionType<
TControllers,
SolutionType.recommendation
>,
InferControllerPropsMapFromDefinitions<TControllers>
>;

/**
* Fetches the hydrated state on the client side using your engine definition and the static state.
*/
hydrateStaticState: HydrateStaticState<
TEngine,
InferControllersMapFromDefinition<
TControllers,
SolutionType.recommendation
>,
UnknownAction,
InferControllerPropsMapFromDefinitions<TControllers>
>;
/**
* Builds an engine and its controllers from an engine definition.
*/
build: BuildWithList<
TEngine,
TEngineOptions,
InferControllersMapFromDefinition<
TControllers,
SolutionType.recommendation
>,
InferControllerPropsMapFromDefinitions<TControllers>
>;

/**
* Sets the navigator context provider.
Expand Down
3 changes: 3 additions & 0 deletions packages/headless/src/app/ssr-engine/types/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import {
OptionsTuple,
} from './common.js';

// can I build them like so ?
export interface BuildOptions<TEngineOptions> {
// c: (keyof TControllersMap)[]; // list of controllers to build, but how did that work ??
//Why is it optional ?????
extend?: OptionsExtender<TEngineOptions>;
}

Expand Down

0 comments on commit f2184e6

Please sign in to comment.