Skip to content

Commit

Permalink
feat: support for two separate applications in hybrid mode (#1289)
Browse files Browse the repository at this point in the history
* feat: support for two separate applications

* docs: describe the new hybridApplication setting

Co-authored-by: Stefan Hauke <[email protected]>
  • Loading branch information
jometzner and shauke authored Oct 12, 2022
1 parent d7bd199 commit 6b99953
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 33 deletions.
24 changes: 9 additions & 15 deletions docs/concepts/hybrid-approach.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A possible scenario would be to have the shopping experience with all its SEO op
- ICM 7.10.32.16-LTS or 7.10.38.6-LTS
- PWA 2.3.0

> **NOTE:** The feature is based on the assumption that the PWA and the ICM can read and write each other's cookies. That means that both cookies must have the same domain and the same path. Therefore, the feature only works if the PWA and the ICM are running in the same domain.
> **NOTE:** The feature is based on the assumption that the PWA and the ICM can read and write each other's cookies. That means that cookies written by the PWA and ICM must have the same domain and the same path. This works since all Responsive Starter Store requests and responses are proxied through the PWA SSR simulating a common domain.
## Architectural Concept

Expand Down Expand Up @@ -55,7 +55,7 @@ The server-side rendering process must be started with `SSR_HYBRID=1`.
In addition, the PWA must be run with secure URLs as well.
To achieve this locally, set the environment variable `SSL=1` and provide a valid certificate (see [SSR Startup](../guides/ssr-startup.md#running-with-https)).

> :warning: **Don't use this option for production environments**, as those should not use the local certificates provide via the `dist`folder.
> :warning: **Don't use this option for production environments**, as those should not use the local certificates provide via the `dist` folder.
> :warning: **Only for development environments**: It might be necessary to set `TRUST_ICM=1` if the used development ICM is deployed with an insecure certificate.
Expand Down Expand Up @@ -88,24 +88,18 @@ However, `pwa` and `icmBuild` are used in the client application where [named ca
## PWA Adaptions

With version 0.23.0 the PWA was changed to no longer reuse the Responsive Starter Store application types but rather be based upon the newly introduced headless application type for REST Clients - `intershop.REST`.
This application type is completely independent of the Responsive Starter Store and is not suitable for storefront setups using the hybrid approach.
For this reason, the PWA must be adapted to work with the Responsive Starter Store again.
This application type is completely independent of the Responsive Starter Store.
For this reason, the PWA must be configured to know which application it has to use to work with the Responsive Starter Store again (`hybridApplication`).

**Migration Steps to prepare the PWA for the hybrid approach with the Responsive Starter Store**
**Steps to prepare the PWA for the hybrid approach with the Responsive Starter Store**

- Use a current PWA version
- Revert the following Git commits:

| commit | comment |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| [50dc72ef0](https://github.com/intershop/intershop-pwa/commit/50dc72ef083d6bee3c33edebef275b85762db618) | feat: switch to the new headless REST application type CMS content model (#302) |
| [741454c8c](https://github.com/intershop/intershop-pwa/commit/741454c8c839dd001a3943236172d75ffd05541d) | feat: switch to the new headless REST application type applications demo content (#302) |

- Configure the correct `icmApplication` setting
- Add needed PWA specific Content Includes in the Responsive Starter Store
- Via `componentEntryPointDefinitions` in the ICM project source code
- Configure `icmApplication` setting to denote the `intershop.REST` based application used by the PWA (this is in the demo scenario just `rest`).
- Configure `hybridApplication` setting to denote the Responsive Starter Store application (this is usually `-`).
- Follow the Hybrid configuration setup

> **NOTE:** If for some reason the CMS content of the Responsive Starter Store should directly be reused in the PWA in a hybrid approach, the PWA needs some code adaptions and has to use the same application as the Responsive Starter Store. For more details see the older version of this documentation - [Hybrid Approach - PWA Adaptions (3.0.0)](https://github.com/intershop/intershop-pwa/blob/3.0.0/docs/concepts/hybrid-approach.md#pwa-adaptions).
# Further References

- [Guide - Building and Running Server-Side Rendering](../guides/ssr-startup.md)
Expand Down
2 changes: 1 addition & 1 deletion nginx/templates/multi-channel.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{{- $application := "" }}{{ if (has . "application") }}{{ $application = join ( slice ";application" .application ) "=" }}{{ end }}
{{- $identityProvider := "" }}{{ if (has . "identityProvider") }}{{ $identityProvider = .identityProvider }}{{ end }}
{{- $lang := "default" }}{{ if (has . "lang") }}{{ $lang = .lang }}{{ end }}
{{- $currency := "" }}{{ if (has . "currency") }}{{ $currency = join ( slice ";currency" .currency ) "=" }}{{ end }}
{{- $currency := "" }}{{ if (has . "currency") }}{{ $currency = .currency }}{{ end }}
{{- $features := "" }}{{ if (has . "features") }}{{ $features = join ( slice ";features" .features ) "=" }}{{ end }}
{{- $addFeatures := "" }}{{ if (has . "addFeatures") }}{{ $addFeatures = join ( slice ";addFeatures" .addFeatures ) "=" }}{{ end }}
{{- $theme := "" }}{{ if (has . "theme") }}{{ $theme = join ( slice ";theme" .theme ) "=" }}{{ end }}
Expand Down
34 changes: 23 additions & 11 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import { join } from 'path';
import * as robots from 'express-robots-txt';
import * as fs from 'fs';
import * as proxy from 'express-http-proxy';
import { AppServerModule, ICM_WEB_URL, HYBRID_MAPPING_TABLE, environment, APP_BASE_HREF } from './src/main.server';
import {
AppServerModule,
ICM_WEB_URL,
HYBRID_MAPPING_TABLE,
environment,
APP_BASE_HREF,
ICM_CONFIG_MATCH,
} from './src/main.server';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { getDeployURLFromEnv, setDeployUrlInFile } from './src/ssr/deploy-url';

Expand Down Expand Up @@ -192,8 +199,16 @@ export function app() {
for (const entry of HYBRID_MAPPING_TABLE) {
const icmUrlRegex = new RegExp(entry.icm);
const pwaUrlRegex = new RegExp(entry.pwa);
if (icmUrlRegex.exec(url) && entry.handledBy === 'pwa') {
newUrl = url.replace(icmUrlRegex, `/${entry.pwaBuild}`);
const icmMatchArray = icmUrlRegex.exec(url);
if (icmMatchArray && entry.handledBy === 'pwa') {
const config: { [is: string]: string } = {
...icmMatchArray.groups,
application: environment.icmApplication || '-',
};
// Rewrite configuration part of incoming ICM url
const _url = url.replace(new RegExp(ICM_CONFIG_MATCH), buildICMWebURL(config));
// Build pwa URL based on equally named-groups of ICM url
newUrl = _url.replace(icmUrlRegex, `/${entry.pwaBuild}`);
break;
} else if (pwaUrlRegex.exec(url) && entry.handledBy === 'icm') {
const config: { [is: string]: string } = {};
Expand All @@ -216,15 +231,9 @@ export function app() {
config.channel = environment.icmChannel;
}

if (/;application=[^;]*/.test(url)) {
config.application = /;application=([^;]*)/.exec(url)[1];
} else {
config.application = environment.icmApplication || '-';
}
config.application = environment.hybridApplication || environment.icmApplication;

const build = [ICM_WEB_URL, entry.icmBuild]
.join('/')
.replace(/\$<(\w+)>/g, (match, group) => config[group] || match);
const build = [buildICMWebURL(config), entry.icmBuild].join('/');
newUrl = url.replace(pwaUrlRegex, build).replace(/;.*/g, '');
break;
}
Expand All @@ -239,6 +248,9 @@ export function app() {
}
};

const buildICMWebURL = (config: { [is: string]: string } = {}): string =>
ICM_WEB_URL.replace(/\$<(\w+)>/g, (match, group) => config[group] || match);

if (process.env.SSR_HYBRID) {
server.use('*', hybridRedirect);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface ConfigurationState {
serverStatic?: string;
channel?: string;
application?: string;
hybridApplication?: string;
identityProvider?: string;
identityProviders?: { [id: string]: { type?: string; [key: string]: unknown } };
features?: string[];
Expand All @@ -34,6 +35,7 @@ const initialState: ConfigurationState = {
serverStatic: undefined,
channel: undefined,
application: undefined,
hybridApplication: undefined,
features: undefined,
addFeatures: [],
defaultLocale: environment.defaultLocale,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { ConfigurationState } from './configuration.reducer';

export const getConfigurationState = createSelector(getCoreState, state => state.configuration);

export const getICMApplication = createSelector(getConfigurationState, state => state.application || '-');
const getICMApplication = createSelector(getConfigurationState, state => state.application || '-');

export const getResponsiveStarterStoreApplication = createSelector(
getConfigurationState,
state => state.hybridApplication || '-'
);

export const getICMServerURL = createSelector(getConfigurationState, state =>
state.baseURL && state.server ? `${state.baseURL}/${state.server}` : undefined
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/store/hybrid/hybrid.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
getConfigurationState,
getCurrentCurrency,
getCurrentLocale,
getICMApplication,
getResponsiveStarterStoreApplication,
} from 'ish-core/store/core/configuration';

import { ICM_WEB_URL } from '../../../../hybrid/default-url-mapping-table';
Expand All @@ -13,7 +13,7 @@ export const getICMWebURL = createSelector(
getConfigurationState,
getCurrentLocale,
getCurrentCurrency,
getICMApplication,
getResponsiveStarterStoreApplication,
(state, locale, currency, application) =>
ICM_WEB_URL.replace('$<channel>', state.channel)
.replace('$<lang>', locale)
Expand Down
2 changes: 2 additions & 0 deletions src/environments/environment.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface Environment {
icmServerStatic: string;
icmChannel: string;
icmApplication?: string;
hybridApplication?: string;

// array of REST path expressions that should always be mocked
apiMockPaths?: string[];
Expand Down Expand Up @@ -138,6 +139,7 @@ export const ENVIRONMENT_DEFAULTS: Omit<Environment, 'icmChannel'> = {
icmServer: 'INTERSHOP/rest/WFS',
icmServerStatic: 'INTERSHOP/static/WFS',
icmApplication: 'rest',
hybridApplication: '-',
identityProvider: 'ICM',

/* FEATURE TOGGLES */
Expand Down
4 changes: 2 additions & 2 deletions src/hybrid/default-url-mapping-table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const ICM_CONFIG_MATCH = `^/INTERSHOP/web/WFS/(?<channel>[\\w-]+)/(?<lang>[\\w-]+)/(?<application>[\\w-]+)/[\\w-]+`;
export const ICM_CONFIG_MATCH = `^/INTERSHOP/web/WFS/(?<channel>[\\w-]+)/(?<lang>[\\w-]+)/(?<application>[\\w-]+)/(?<currency>[\\w-]+)`;

const PWA_CONFIG_BUILD = ';channel=$<channel>;lang=$<lang>;application=$<application>';

Expand Down Expand Up @@ -99,7 +99,7 @@ export const HYBRID_MAPPING_TABLE: HybridMappingEntry[] = [
pwaBuild: `account${PWA_CONFIG_BUILD}`,
pwa: '^/account.*$',
icmBuild: 'ViewUserAccount-Start',
handledBy: 'icm',
handledBy: 'pwa',
},
{
id: 'Register',
Expand Down
2 changes: 1 addition & 1 deletion src/main.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ if (PRODUCTION_MODE) {

export { AppServerModule } from './app/app.server.module';
export { environment } from './environments/environment';
export { HYBRID_MAPPING_TABLE, ICM_WEB_URL } from './hybrid/default-url-mapping-table';
export { HYBRID_MAPPING_TABLE, ICM_WEB_URL, ICM_CONFIG_MATCH } from './hybrid/default-url-mapping-table';
export { APP_BASE_HREF } from '@angular/common';

0 comments on commit 6b99953

Please sign in to comment.