Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: change OCI punchout URL to ICM Pipeline #1702

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/guides/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ kb_sync_latest_only

# Migrations

## From 5.2 to 5.3

With ICM 12.2.0, 11.11.1 or 7.10.41.3 the ICM server itself provides an OCI punchout URL (Pipeline) that works better for the OCI punchout functions `BACKGROUND_SEARCH` and `VALIDATE` than the similar now deprecated functionality in the PWA.
For that reason the provided OCI Punchout URL is now pointing to the ICM pipeline `ViewOCICatalogPWA-Start` that handles the different functionalities and redirects to the PWA (configured as _External Base URL_ in ICM) only for catalog browsing and the detail function.

## From 5.1 to 5.2

> [!NOTE]
Expand Down
2 changes: 2 additions & 0 deletions src/app/core/facades/app.facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getCurrentLocale,
getDeviceType,
getICMBaseURL,
getPipelineEndpoint,
getRestEndpoint,
} from 'ish-core/store/core/configuration';
import { businessError, getGeneralError, getGeneralErrorType } from 'ish-core/store/core/error';
Expand Down Expand Up @@ -44,6 +45,7 @@ export class AppFacade {
breadcrumbData$ = this.store.pipe(select(getBreadcrumbData));

getRestEndpoint$ = this.store.pipe(select(getRestEndpoint));
getPipelineEndpoint$ = this.store.pipe(select(getPipelineEndpoint));

appWrapperClasses$ = combineLatest([
this.store.pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export class ConfigurationEffects {
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_BASE_URL', 'icmBaseURL'),
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_SERVER', 'icmServer'),
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_SERVER_STATIC', 'icmServerStatic'),
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_SERVER_WEB', 'icmServerWeb'),
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_CHANNEL', 'icmChannel'),
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_APPLICATION', 'icmApplication'),
this.stateProperties
Expand All @@ -91,6 +92,7 @@ export class ConfigurationEffects {
baseURL,
server,
serverStatic,
serverWeb,
channel,
application,
features,
Expand All @@ -102,6 +104,7 @@ export class ConfigurationEffects {
baseURL,
server,
serverStatic,
serverWeb,
channel,
application,
features,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ConfigurationState {
baseURL?: string;
server?: string;
serverStatic?: string;
serverWeb?: string;
channel?: string;
application?: string;
hybridApplication?: string;
Expand All @@ -34,6 +35,7 @@ const initialState: ConfigurationState = {
baseURL: undefined,
server: undefined,
serverStatic: undefined,
serverWeb: undefined,
channel: undefined,
application: undefined,
hybridApplication: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getICMServerURL,
getICMStaticURL,
getIdentityProvider,
getPipelineEndpoint,
getRestEndpoint,
} from './configuration.selectors';

Expand All @@ -42,13 +43,14 @@ describe('Configuration Selectors', () => {

describe('initial state', () => {
it('should be undefined or empty values for most selectors', () => {
expect(getRestEndpoint(store$.state)).toBeUndefined();
expect(getICMBaseURL(store$.state)).toBeUndefined();
expect(getICMServerURL(store$.state)).toBeUndefined();
expect(getICMStaticURL(store$.state)).toBeUndefined();
expect(getFeatures(store$.state)).toBeUndefined();
expect(getDeviceType(store$.state)).toBeUndefined();
expect(getIdentityProvider(store$.state)).toBeUndefined();
expect(getRestEndpoint(store$.state)).toBeUndefined();
expect(getPipelineEndpoint(store$.state)).toBeUndefined();
});
});

Expand All @@ -59,18 +61,32 @@ describe('Configuration Selectors', () => {
baseURL: 'http://example.org',
server: 'api',
serverStatic: 'static',
serverWeb: 'web',
channel: 'site',
features: ['compare', 'recently'],
})
);
store$.dispatch(
loadServerConfigSuccess({
config: {
general: {
defaultLocale: 'en_US',
defaultCurrency: 'USD',
locales: ['en_US', 'fr_BE', 'de_DE'],
currencies: ['USD', 'EUR'],
},
},
})
);
});

it('should have defined values for all selectors', () => {
expect(getRestEndpoint(store$.state)).toEqual('http://example.org/api/site/-');
expect(getICMBaseURL(store$.state)).toEqual('http://example.org');
expect(getICMServerURL(store$.state)).toEqual('http://example.org/api');
expect(getICMStaticURL(store$.state)).toEqual('http://example.org/static/site/-');
expect(getFeatures(store$.state)).toIncludeAllMembers(['compare', 'recently']);
expect(getRestEndpoint(store$.state)).toEqual('http://example.org/api/site/-');
expect(getPipelineEndpoint(store$.state)).toEqual('http://example.org/web/site/en_US/-/USD');
});

describe('after setting application', () => {
Expand All @@ -83,11 +99,12 @@ describe('Configuration Selectors', () => {
});

it('should have defined values for all selectors', () => {
expect(getRestEndpoint(store$.state)).toEqual('http://example.org/api/site/app');
expect(getICMBaseURL(store$.state)).toEqual('http://example.org');
expect(getICMServerURL(store$.state)).toEqual('http://example.org/api');
expect(getICMStaticURL(store$.state)).toEqual('http://example.org/static/site/app');
expect(getFeatures(store$.state)).toIncludeAllMembers(['compare', 'recently']);
expect(getRestEndpoint(store$.state)).toEqual('http://example.org/api/site/app');
expect(getPipelineEndpoint(store$.state)).toEqual('http://example.org/web/site/en_US/app/USD');
});
});

Expand Down
26 changes: 20 additions & 6 deletions src/app/core/store/core/configuration/configuration.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ export const getICMServerURL = createSelector(getConfigurationState, state =>
state.baseURL && state.server ? `${ssrBaseUrl || state.baseURL}/${state.server}` : undefined
);

export const getRestEndpoint = createSelector(
getICMServerURL,
getConfigurationState,
getICMApplication,
(serverUrl, state, application) =>
serverUrl && state.channel ? `${serverUrl}/${state.channel}/${application}` : undefined
const getICMServerWebURL = createSelector(getConfigurationState, state =>
state.baseURL && state.serverWeb ? `${ssrBaseUrl || state.baseURL}/${state.serverWeb}` : undefined
);

export const getICMStaticURL = createSelector(getConfigurationState, getICMApplication, (state, application) =>
Expand Down Expand Up @@ -136,3 +132,21 @@ export const getMultiSiteLocaleMap = createSelector(
getConfigurationState,
(state: ConfigurationState) => state.multiSiteLocaleMap
);

export const getRestEndpoint = createSelector(
getICMServerURL,
getConfigurationState,
getICMApplication,
(serverUrl, state, application) =>
serverUrl && state.channel ? `${serverUrl}/${state.channel}/${application}` : undefined
);

export const getPipelineEndpoint = createSelector(
getICMServerWebURL,
getConfigurationState,
getCurrentLocale,
getICMApplication,
getCurrentCurrency,
(serverUrl, state, locale, application, currency) =>
serverUrl && state.channel ? `${serverUrl}/${state.channel}/${locale}/${application}/${currency}` : undefined
);
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable etc/no-deprecated */
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
import { ActivatedRouteSnapshot, Params, Router, UrlTree, convertToParamMap } from '@angular/router';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable etc/no-deprecated */
import { HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router';
Expand Down Expand Up @@ -178,7 +179,7 @@ export class PunchoutIdentityProvider implements IdentityProvider {
if (route.queryParamMap.get('FUNCTION') === 'DETAIL' && route.queryParamMap.get('PRODUCTID')) {
return of(this.router.parseUrl(`/product/${route.queryParamMap.get('PRODUCTID')}`));

// Validation of Products
// Validation of Products - @deprecated This functionality should be handled by the ICM pipeline `ViewOCICatalogPWA-Start`.
} else if (route.queryParamMap.get('FUNCTION') === 'VALIDATE' && route.queryParamMap.get('PRODUCTID')) {
return this.punchoutService
.getOciPunchoutProductData(route.queryParamMap.get('PRODUCTID'), route.queryParamMap.get('QUANTITY') || '1')
Expand All @@ -187,7 +188,7 @@ export class PunchoutIdentityProvider implements IdentityProvider {
map(() => false)
);

// Background Search
// Background Search - @deprecated: is now handled by the ICM ViewOCICatalogPWA-Start pipeline
} else if (route.queryParamMap.get('FUNCTION') === 'BACKGROUND_SEARCH' && route.queryParamMap.get('SEARCHSTRING')) {
return this.punchoutService.getOciPunchoutSearchData(route.queryParamMap.get('SEARCHSTRING')).pipe(
tap(data => this.punchoutService.submitOciPunchoutData(data, false)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ <h2 class="mt-1">
<p>
{{ 'account.punchout.oci.info.url.helptext' | translate }}
</p>
<p>{{ ociPunchoutUrl }}</p>
<p>{{ ociPunchoutUrl$ | async }}</p>
</ng-template>
<ng-template [ngSwitchCase]="'cxml'">
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ describe('Account Punchout Page Component', () => {
when(punchoutFacade.punchoutUsersByRoute$()).thenReturn(of(users));
when(punchoutFacade.selectedPunchoutType$).thenReturn(of('oci'));
when(appFacade.getRestEndpoint$).thenReturn(of('https://myBaseServerURL/INTERSHOP/rest/WFS/myChannel/rest'));
when(appFacade.getPipelineEndpoint$).thenReturn(
of('https://myBaseServerURL/INTERSHOP/web/WFS/myChannel/en_US/rest/USD')
);
when(punchoutFacade.punchoutLoading$).thenReturn(of(false));
});

Expand Down Expand Up @@ -114,4 +117,15 @@ describe('Account Punchout Page Component', () => {
done();
});
});

it('should determine oci punchout url on init', done => {
fixture.detectChanges();

component.ociPunchoutUrl$.subscribe(url => {
expect(url).toMatchInlineSnapshot(
`"https://myBaseServerURL/INTERSHOP/web/WFS/myChannel/en_US/rest/USD/ViewOCICatalogPWA-Start?USERNAME=<USERNAME>&PASSWORD=<PASSWORD>&HOOK_URL=<HOOK_URL>"`
);
done();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export class AccountPunchoutPageComponent implements OnInit {
loading$: Observable<boolean>;
error$: Observable<HttpError>;

ociPunchoutUrl = `${window.location.origin}/punchout?USERNAME=<USERNAME>&PASSWORD=<PASSWORD>&HOOK_URL=<HOOK_URL>`;
cxmlPunchoutUrl$: Observable<string>;
ociPunchoutUrl$: Observable<string>;

constructor(
private accountFacade: AccountFacade,
Expand All @@ -42,6 +42,9 @@ export class AccountPunchoutPageComponent implements OnInit {
withLatestFrom(this.accountFacade.customer$),
map(([url, customer]) => `${url}/customers/${customer?.customerNo}/punchouts/cxml1.2/setuprequest`)
);
this.ociPunchoutUrl$ = this.appFacade.getPipelineEndpoint$.pipe(
map(url => `${url}/ViewOCICatalogPWA-Start?USERNAME=<USERNAME>&PASSWORD=<PASSWORD>&HOOK_URL=<HOOK_URL>`)
);
}

deleteUser(user: PunchoutUser) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ export class PunchoutService {
*
* @param productId The product id (SKU) of the product to validate.
* @param quantity The quantity for the validation (default: '1').
*
* @deprecated This functionality should be handled by the ICM pipeline `ViewOCICatalogPWA-Start`.
*/
getOciPunchoutProductData(productId: string, quantity = '1'): Observable<Attribute<string>[]> {
if (!productId) {
Expand Down Expand Up @@ -420,6 +422,8 @@ export class PunchoutService {
* Gets a JSON object with the necessary OCI punchout data for the background search.
*
* @param searchString The search string to search punchout products.
*
* @deprecated This functionality should be handled by the ICM pipeline `ViewOCICatalogPWA-Start`.
*/
getOciPunchoutSearchData(searchString: string): Observable<Attribute<string>[]> {
if (!searchString) {
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 @@ -12,6 +12,7 @@ export interface Environment {
icmBaseURL: string;
icmServer: string;
icmServerStatic: string;
icmServerWeb: string;
icmChannel: string;
icmApplication?: string;
hybridApplication?: string;
Expand Down Expand Up @@ -148,6 +149,7 @@ export const ENVIRONMENT_DEFAULTS: Omit<Environment, 'icmChannel'> = {
icmBaseURL: 'https://develop.icm.intershop.de',
icmServer: 'INTERSHOP/rest/WFS',
icmServerStatic: 'INTERSHOP/static/WFS',
icmServerWeb: 'INTERSHOP/web/WFS',
icmApplication: '-',

/* FEATURE TOGGLES */
Expand Down
Loading