diff --git a/projects/core/src/cms/cms.module.ts b/projects/core/src/cms/cms.module.ts index 5a7eab7a7b5..bf4315b6c46 100755 --- a/projects/core/src/cms/cms.module.ts +++ b/projects/core/src/cms/cms.module.ts @@ -5,11 +5,32 @@ */ import { ModuleWithProviders, NgModule } from '@angular/core'; -import { provideDefaultConfig } from '../config/config-providers'; -import { defaultCmsModuleConfig } from './config/default-cms-config'; +import { + defaultCmsModuleConfig, + defaultUserCmsModuleConfig, +} from './config/default-cms-config'; import { CmsService } from './facade/cms.service'; import { PageMetaModule } from './page/page-meta.module'; import { CmsStoreModule } from './store/cms-store.module'; +import { ConfigChunk, provideDefaultConfigFactory } from '../config'; +import { USER_CMS_ENDPOINTS } from '../model'; + +function getDefaultCmsConfig(configChunk: any) { + let isUserCmsEndpoint = false; + + configChunk.find((config: any) => { + const userCmsEndpoints = config.features?.[USER_CMS_ENDPOINTS]; + + if (Boolean(userCmsEndpoints)) { + isUserCmsEndpoint = userCmsEndpoints; + } + }); + + if (isUserCmsEndpoint) { + return defaultUserCmsModuleConfig; + } + return defaultCmsModuleConfig; +} @NgModule({ imports: [CmsStoreModule, PageMetaModule.forRoot()], @@ -18,7 +39,11 @@ export class CmsModule { static forRoot(): ModuleWithProviders { return { ngModule: CmsModule, - providers: [CmsService, provideDefaultConfig(defaultCmsModuleConfig)], + providers: [ + CmsService, + // TODO: (CXSPA-4886) In the major change to provideDefaultConfig(defaultCmsModuleConfig) + provideDefaultConfigFactory(getDefaultCmsConfig, [ConfigChunk]), + ], }; } } diff --git a/projects/core/src/cms/config/default-cms-config.ts b/projects/core/src/cms/config/default-cms-config.ts index 78d9bd9a5f7..8422ce0bf18 100644 --- a/projects/core/src/cms/config/default-cms-config.ts +++ b/projects/core/src/cms/config/default-cms-config.ts @@ -22,3 +22,18 @@ export const defaultCmsModuleConfig: CmsConfig = { pageSize: 50, }, }; + +// TODO: (CXSPA-4886) replace and remove this with defaultCmsModuleConfig in the major +export const defaultUserCmsModuleConfig: CmsConfig = { + ...defaultCmsModuleConfig, + backend: { + occ: { + endpoints: { + component: 'users/${userId}/cms/components/${id}', + components: 'users/${userId}/cms/components', + pages: 'users/${userId}/cms/pages', + page: 'users/${userId}/cms/pages/${id}', + }, + }, + }, +}; diff --git a/projects/core/src/model/cms.model.ts b/projects/core/src/model/cms.model.ts index 8036808a331..0b7b7b62b79 100644 --- a/projects/core/src/model/cms.model.ts +++ b/projects/core/src/model/cms.model.ts @@ -249,3 +249,6 @@ export interface CmsPickupItemDetails extends CmsComponent { showEdit: boolean; context: string; } + +// TODO: (CXSPA-4886) Remove this flag in the major +export const USER_CMS_ENDPOINTS = 'userCmsEndpoints'; diff --git a/projects/core/src/occ/adapters/cms/legacy-occ-cms-component.adapter.spec.ts b/projects/core/src/occ/adapters/cms/legacy-occ-cms-component.adapter.spec.ts index 5d96838a7ef..871b8b71113 100644 --- a/projects/core/src/occ/adapters/cms/legacy-occ-cms-component.adapter.spec.ts +++ b/projects/core/src/occ/adapters/cms/legacy-occ-cms-component.adapter.spec.ts @@ -33,7 +33,7 @@ const pageContext: PageContext = { type: PageType.PRODUCT_PAGE, }; -const spyOnPostEndpoint = `/cms/components?productCode=123`; +const spyOnPostEndpoint = `userId/anonymous/cms/components?productCode=123`; describe('LegacyOccCmsComponentAdapter', () => { let service: LegacyOccCmsComponentAdapter; @@ -72,7 +72,7 @@ describe('LegacyOccCmsComponentAdapter', () => { const testRequest = mockHttpRequest( 'POST', - '/cms/components?productCode=123' + 'userId/anonymous/cms/components?productCode=123' ); assertPostTestRequestBody(testRequest); diff --git a/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.spec.ts b/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.spec.ts index 24ad42fa5bd..837150febdd 100644 --- a/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.spec.ts +++ b/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.spec.ts @@ -12,6 +12,9 @@ import { ConverterService } from '../../../util/converter.service'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; import { OccCmsComponentAdapter } from './occ-cms-component.adapter'; +import { Observable, of } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { FeatureConfigService, UserIdService } from '@spartacus/core'; const components: CmsComponent[] = [ { uid: 'comp1', typeCode: 'SimpleBannerComponent' }, @@ -29,6 +32,8 @@ class CmsStructureConfigServiceMock {} const endpoint = '/cms'; +const userEndpoint = '/users/${userId}/cms'; + class OccEndpointsServiceMock { buildUrl(_endpoint: string, _urlParams?: any, _queryParams?: any): string { return ''; @@ -42,11 +47,11 @@ const context: PageContext = { const ids = ['comp_uid1', 'comp_uid2']; -const spyOnLoadEndpoint = - endpoint + `/components/comp1?productCode=${context.id}`; +const spyOnLoadEndpoint = (_endpoint = endpoint) => + _endpoint + `/components/comp1?productCode=${context.id}`; -const spyOnGetEndpoint = - endpoint + +const spyOnGetEndpoint = (_endpoint = endpoint) => + _endpoint + `/components?componentIds=${ids.toString()}&productCode=${context.id}`; describe('OccCmsComponentAdapter', () => { @@ -54,12 +59,15 @@ describe('OccCmsComponentAdapter', () => { let httpMock: HttpTestingController; let converter: ConverterService; let endpointsService: OccEndpointsService; + let userIdService: UserIdService; + let featureConfigService: FeatureConfigService; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [ OccCmsComponentAdapter, + UserIdService, { provide: OccEndpointsService, useClass: OccEndpointsServiceMock }, { provide: CmsStructureConfigService, @@ -71,6 +79,8 @@ describe('OccCmsComponentAdapter', () => { httpMock = TestBed.inject(HttpTestingController); converter = TestBed.inject(ConverterService); endpointsService = TestBed.inject(OccEndpointsService); + userIdService = TestBed.inject(UserIdService); + featureConfigService = TestBed.inject(FeatureConfigService); spyOn(converter, 'pipeable').and.callThrough(); spyOn(converter, 'pipeableMany').and.callThrough(); @@ -80,67 +90,168 @@ describe('OccCmsComponentAdapter', () => { httpMock.verify(); }); - describe('load', () => { - it('should get cms component data', () => { - spyOnEndpoint(spyOnLoadEndpoint); + describe('user endpoints', () => { + beforeEach(() => { + spyOn(featureConfigService, 'isEnabled').and.returnValue(true); + }); - service.load('comp1', context).subscribe((result) => { - expect(result).toEqual(component); - }); + describe('load', () => { + it('should get cms component data', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOnEndpoint(spyOnLoadEndpoint(userEndpoint)); + + service.load('comp1', context).subscribe((result) => { + expect(result).toEqual(component); + }); + + const testRequest = mockHttpRequest( + 'GET', + spyOnLoadEndpoint(userEndpoint) + ); - const testRequest = mockHttpRequest('GET', spyOnLoadEndpoint); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('component', { + urlParams: { id: 'comp1', userId: 'anonymous' }, + queryParams: { productCode: '123' }, + }); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('component', { - urlParams: { id: 'comp1' }, - queryParams: { productCode: '123' }, + assertTestRequest(testRequest, component); + done(); }); - assertTestRequest(testRequest, component); + it('should use normalizer', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOnEndpoint(spyOnLoadEndpoint(userEndpoint)); + + service.load('comp1', context).subscribe(); + + assertNormalizer(spyOnLoadEndpoint(userEndpoint)); + + expect(converter.pipeable).toHaveBeenCalledWith( + CMS_COMPONENT_NORMALIZER + ); + done(); + }); }); - it('should use normalizer', () => { - spyOnEndpoint(spyOnLoadEndpoint); + describe('load list of cms component data using GET request', () => { + it('should get a list of cms component data using GET request without pagination parameters', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOnEndpoint(spyOnGetEndpoint(userEndpoint)); + + assertGetSubscription(service).subscribe(); + const testRequest = mockHttpRequest( + 'GET', + spyOnGetEndpoint(userEndpoint) + ); - service.load('comp1', context).subscribe(); + assertGetRequestGetUrl('DEFAULT', '2', true); - assertNormalizer(spyOnLoadEndpoint); + assertTestRequest(testRequest, componentList); + done(); + }); + + it('should get a list of cms component data using GET request with pagination parameters', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOnEndpoint(spyOnGetEndpoint(userEndpoint)); + + assertGetSubscription(service, 'FULL', 0, 5).subscribe(); + const testRequest = mockHttpRequest( + 'GET', + spyOnGetEndpoint(userEndpoint) + ); + + assertGetRequestGetUrl('FULL', '5', true); + + assertTestRequest(testRequest, componentList); + done(); + }); + + it('should use normalizer', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOnEndpoint(spyOnGetEndpoint(userEndpoint)); - expect(converter.pipeable).toHaveBeenCalledWith(CMS_COMPONENT_NORMALIZER); + assertGetSubscription(service).subscribe(); + assertNormalizer(spyOnGetEndpoint(userEndpoint)); + assertConverterPipeableMany(); + done(); + }); }); }); - describe('load list of cms component data using GET request', () => { - it('should get a list of cms component data using GET request without pagination parameters', () => { - spyOnEndpoint(spyOnGetEndpoint); + describe('default endpoints', () => { + beforeEach(() => { + spyOn(featureConfigService, 'isEnabled').and.returnValue(false); + }); - assertGetSubscription(service); + describe('load', () => { + it('should get cms component data', (done) => { + spyOnEndpoint(spyOnLoadEndpoint()); - const testRequest = mockHttpRequest('GET', spyOnGetEndpoint); + service.load('comp1', context).subscribe((result) => { + expect(result).toEqual(component); + }); - assertGetRequestGetUrl('DEFAULT', '2'); + const testRequest = mockHttpRequest('GET', spyOnLoadEndpoint()); - assertTestRequest(testRequest, componentList); - }); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('component', { + urlParams: { id: 'comp1' }, + queryParams: { productCode: '123' }, + }); - it('should get a list of cms component data using GET request with pagination parameters', () => { - spyOnEndpoint(spyOnGetEndpoint); + assertTestRequest(testRequest, component); + done(); + }); - assertGetSubscription(service, 'FULL', 0, 5); + it('should use normalizer', (done) => { + spyOnEndpoint(spyOnLoadEndpoint()); - const testRequest = mockHttpRequest('GET', spyOnGetEndpoint); + service.load('comp1', context).subscribe(); - assertGetRequestGetUrl('FULL', '5'); + assertNormalizer(spyOnLoadEndpoint()); - assertTestRequest(testRequest, componentList); + expect(converter.pipeable).toHaveBeenCalledWith( + CMS_COMPONENT_NORMALIZER + ); + done(); + }); }); - it('should use normalizer', () => { - spyOnEndpoint(spyOnGetEndpoint); + describe('load list of cms component data using GET request', () => { + it('should get a list of cms component data using GET request without pagination parameters', (done) => { + spyOnEndpoint(spyOnGetEndpoint()); + + assertGetSubscription(service).subscribe(); + + const testRequest = mockHttpRequest('GET', spyOnGetEndpoint()); + + assertGetRequestGetUrl('DEFAULT', '2'); + + assertTestRequest(testRequest, componentList); + done(); + }); + + it('should get a list of cms component data using GET request with pagination parameters', (done) => { + spyOnEndpoint(spyOnGetEndpoint()); + + assertGetSubscription(service, 'FULL', 0, 5).subscribe(); + + const testRequest = mockHttpRequest('GET', spyOnGetEndpoint()); - assertGetSubscription(service); + assertGetRequestGetUrl('FULL', '5'); - assertNormalizer(spyOnGetEndpoint); - assertConverterPipeableMany(); + assertTestRequest(testRequest, componentList); + done(); + }); + + it('should use normalizer', (done) => { + spyOnEndpoint(spyOnGetEndpoint()); + + assertGetSubscription(service).subscribe(); + + assertNormalizer(spyOnGetEndpoint()); + assertConverterPipeableMany(); + done(); + }); }); }); @@ -166,16 +277,30 @@ describe('OccCmsComponentAdapter', () => { testRequest.flush(componentObj); } - function assertGetRequestGetUrl(fields: string, pageSize: string) { - expect(endpointsService.buildUrl).toHaveBeenCalledWith('components', { - queryParams: { - fields, - componentIds: ids.toString(), - productCode: '123', - currentPage: '0', - pageSize, - }, - }); + function assertGetRequestGetUrl( + fields: string, + pageSize: string, + isUserId = false + ) { + const queryParams = { + fields, + componentIds: ids.toString(), + productCode: '123', + currentPage: '0', + pageSize, + }; + + expect(endpointsService.buildUrl).toHaveBeenCalledWith( + 'components', + isUserId + ? { + queryParams, + urlParams: { userId: 'anonymous' }, + } + : { + queryParams, + } + ); } function assertConverterPipeableMany() { @@ -193,11 +318,13 @@ describe('OccCmsComponentAdapter', () => { fields?: string, currentPage?: number, pageSize?: number - ) { - adapter + ): Observable { + return adapter .findComponentsByIds(ids, context, fields, currentPage, pageSize) - .subscribe((result) => { - expect(result).toEqual(componentList.component); - }); + .pipe( + tap((result) => { + expect(result).toEqual(componentList.component); + }) + ); } }); diff --git a/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.ts b/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.ts index 89f8d29c498..1abd69c33e5 100644 --- a/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.ts +++ b/projects/core/src/occ/adapters/cms/occ-cms-component.adapter.ts @@ -5,21 +5,29 @@ */ import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { CmsComponentAdapter } from '../../../cms/connectors/component/cms-component.adapter'; import { CMS_COMPONENT_NORMALIZER } from '../../../cms/connectors/component/converters'; -import { CmsComponent, PageType } from '../../../model/cms.model'; +import { + CmsComponent, + PageType, + USER_CMS_ENDPOINTS, +} from '../../../model/cms.model'; import { PageContext } from '../../../routing'; import { ConverterService } from '../../../util/converter.service'; import { Occ } from '../../occ-models/occ.models'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; +import { UserIdService } from '../../../auth'; +import { FeatureConfigService } from '../../../features-config'; @Injectable({ providedIn: 'root', }) export class OccCmsComponentAdapter implements CmsComponentAdapter { + protected readonly userIdService = inject(UserIdService); + protected readonly featureConfigService = inject(FeatureConfigService); protected headers = new HttpHeaders().set('Content-Type', 'application/json'); constructor( @@ -32,6 +40,20 @@ export class OccCmsComponentAdapter implements CmsComponentAdapter { id: string, pageContext: PageContext ): Observable { + // TODO: (CXSPA-4886) Remove flag in the major + if (this.featureConfigService.isEnabled(USER_CMS_ENDPOINTS)) { + return this.userIdService.getUserId().pipe( + switchMap((userId: string) => { + return this.http.get( + this.getComponentEndPoint(id, pageContext, userId), + { + headers: this.headers, + } + ); + }), + this.converter.pipeable(CMS_COMPONENT_NORMALIZER) + ); + } return this.http .get(this.getComponentEndPoint(id, pageContext), { headers: this.headers, @@ -53,7 +75,21 @@ export class OccCmsComponentAdapter implements CmsComponentAdapter { }; requestParams['componentIds'] = ids.toString(); - + // TODO: (CXSPA-4886) Remove flag in the major + if (this.featureConfigService.isEnabled(USER_CMS_ENDPOINTS)) { + return this.userIdService.getUserId().pipe( + switchMap((userId: string) => { + return this.http.get( + this.getComponentsEndpoint(requestParams, fields, userId), + { + headers: this.headers, + } + ); + }), + map((componentList) => componentList.component ?? []), + this.converter.pipeableMany(CMS_COMPONENT_NORMALIZER) + ); + } return this.http .get( this.getComponentsEndpoint(requestParams, fields), @@ -67,14 +103,47 @@ export class OccCmsComponentAdapter implements CmsComponentAdapter { ); } - protected getComponentEndPoint(id: string, pageContext: PageContext): string { + protected getComponentEndPoint( + id: string, + pageContext: PageContext, + userId?: string + ): string { + // TODO: (CXSPA-4886) Remove flag in the major + if (this.featureConfigService.isEnabled(USER_CMS_ENDPOINTS)) { + const queryParams = this.getContextParams(pageContext); + const attributes = userId + ? { + urlParams: { id, userId }, + queryParams, + } + : { urlParams: { id }, queryParams }; + + return this.occEndpoints.buildUrl('component', attributes); + } return this.occEndpoints.buildUrl('component', { urlParams: { id }, queryParams: this.getContextParams(pageContext), }); } - protected getComponentsEndpoint(requestParams: any, fields: string): string { + protected getComponentsEndpoint( + requestParams: any, + fields: string, + userId?: string + ): string { + // TODO: (CXSPA-4886) Remove flag in the major + if (this.featureConfigService.isEnabled(USER_CMS_ENDPOINTS)) { + const queryParams = { fields, ...requestParams }; + + const attributes = userId + ? { + urlParams: { userId }, + queryParams, + } + : { queryParams }; + + return this.occEndpoints.buildUrl('components', attributes); + } return this.occEndpoints.buildUrl('components', { queryParams: { fields, ...requestParams }, }); diff --git a/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.spec.ts b/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.spec.ts index a0113095219..e73a6cd2d1d 100644 --- a/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.spec.ts +++ b/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.spec.ts @@ -10,6 +10,8 @@ import { HOME_PAGE_CONTEXT, PageContext } from '../../../routing'; import { ConverterService } from '../../../util/converter.service'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; import { OccCmsPageAdapter } from './occ-cms-page.adapter'; +import { FeatureConfigService, UserIdService } from '@spartacus/core'; +import { of } from 'rxjs'; import createSpy = jasmine.createSpy; const components: CmsComponent[] = [ @@ -33,6 +35,8 @@ class CmsStructureConfigServiceMock {} const endpoint = '/cms'; +const userEndpoint = '/users/${userId}/cms'; + class OccEndpointsServiceMock { buildUrl(_endpoint: string, _urlParams?: any, _queryParams?: any): string { return ''; @@ -71,12 +75,15 @@ describe('OccCmsPageAdapter', () => { let service: OccCmsPageAdapter; let httpMock: HttpTestingController; let endpointsService: OccEndpointsService; + let userIdService: UserIdService; + let featureConfigService: FeatureConfigService; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [ OccCmsPageAdapter, + UserIdService, { provide: OccEndpointsService, useClass: OccEndpointsServiceMock }, { provide: CmsStructureConfigService, @@ -88,183 +95,358 @@ describe('OccCmsPageAdapter', () => { service = TestBed.inject(OccCmsPageAdapter); httpMock = TestBed.inject(HttpTestingController); endpointsService = TestBed.inject(OccEndpointsService); + userIdService = TestBed.inject(UserIdService); + featureConfigService = TestBed.inject(FeatureConfigService); }); afterEach(() => { httpMock.verify(); }); - describe('endpoint configuration', () => { - it('should get cms home page by specific context', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(homePageContext); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: {}, - }); + describe('user endpoints', () => { + beforeEach(() => { + spyOn(featureConfigService, 'isEnabled').and.returnValue(true); }); - it('should get cms pages by page type and id for any page', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(contentPageContext); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: { - pageLabelOrId: contentPageContext.id, - pageType: PageType.CONTENT_PAGE, - }, + describe('endpoint configuration', () => { + it('should get cms home page by specific context', (done) => { + spyOn(endpointsService, 'buildUrl'); + + service.load(homePageContext).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: {}, + }); + }); + done(); }); - }); - it('should get cms pages by page type and id for any page', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(homePageContext); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: {}, + it('should get cms pages by page type and id for any page', (done) => { + spyOn(endpointsService, 'buildUrl'); + service.load(contentPageContext).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageLabelOrId: contentPageContext.id, + pageType: PageType.CONTENT_PAGE, + }, + }); + }); + done(); }); - }); - it('should get cms product page by product code and ProductPage type', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(productPageContext); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: { - pageType: PageType.PRODUCT_PAGE, - code: productPageContext.id, - }, + it('should get cms pages by page type and id for any page', (done) => { + spyOn(endpointsService, 'buildUrl'); + service.load(homePageContext).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: {}, + }); + }); + done(); }); - }); - it('should get cms category page by category code and CategoryPage type', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(categoryPageContext); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: { - pageType: PageType.CATEGORY_PAGE, - code: categoryPageContext.id, - }, + it('should get cms product page by product code and ProductPage type', (done) => { + spyOn(endpointsService, 'buildUrl'); + service.load(productPageContext).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: PageType.PRODUCT_PAGE, + code: productPageContext.id, + }, + }); + }); + done(); + }); + + it('should get cms category page by category code and CategoryPage type', (done) => { + spyOn(endpointsService, 'buildUrl'); + service.load(categoryPageContext).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: PageType.CATEGORY_PAGE, + code: categoryPageContext.id, + }, + }); + }); + done(); }); - }); - it('should get cms page by pageId if there is no PageType', () => { - spyOn(endpointsService, 'buildUrl'); - service.load(contextWithoutType); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('page', { - urlParams: { id: contextWithoutType.id }, + it('should get cms page by pageId if there is no PageType', (done) => { + spyOn(endpointsService, 'buildUrl'); + service.load(contextWithoutType).subscribe(() => { + expect(endpointsService.buildUrl).toHaveBeenCalledWith('page', { + urlParams: { id: contextWithoutType.id }, + }); + }); + done(); }); }); - }); - describe('http', () => { - it('Should get home page', () => { - spyOn(endpointsService, 'buildUrl').and.returnValue(endpoint + `/pages`); + describe('http', () => { + it('Should get home page', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOn(endpointsService, 'buildUrl').and.returnValue( + userEndpoint + `/pages` + ); + + service.load(homePageContext).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); - service.load(homePageContext).subscribe((result) => { - expect(result).toEqual(cmsPageData); - }); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: {}, + urlParams: { userId: 'anonymous' }, + }); - const testRequest = httpMock.expectOne((req) => { - return req.method === 'GET' && req.url === endpoint + `/pages`; - }); + const testRequest = httpMock.expectOne((req) => { + return req.method === 'GET' && req.url === userEndpoint + `/pages`; + }); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: {}, + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); + done(); }); - expect(testRequest.cancelled).toBeFalsy(); - expect(testRequest.request.responseType).toEqual('json'); - testRequest.flush(cmsPageData); - }); - it('Should get cms content page data', () => { - spyOn(endpointsService, 'buildUrl').and.returnValue( - endpoint + - `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` - ); + it('Should get cms content page data', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOn(endpointsService, 'buildUrl').and.returnValue( + userEndpoint + + `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` + ); - service.load(contentPageContext).subscribe((result) => { - expect(result).toEqual(cmsPageData); + service.load(contentPageContext).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === + userEndpoint + + `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` + ); + }); + + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: contentPageContext.type, + pageLabelOrId: contentPageContext.id, + }, + urlParams: { userId: 'anonymous' }, + }); + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); + done(); }); - const testRequest = httpMock.expectOne((req) => { - return ( - req.method === 'GET' && - req.url === - endpoint + - `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` + it('should get cms product page data', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOn(endpointsService, 'buildUrl').and.returnValue( + userEndpoint + + `/pages?pageType=${productPageContext.type}&code=${productPageContext.id}` ); + service.load(productPageContext).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === + userEndpoint + + `/pages?pageType=${productPageContext.type}&code=${productPageContext.id}` + ); + }); + + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: productPageContext.type, + code: productPageContext.id, + }, + urlParams: { userId: 'anonymous' }, + }); + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); + done(); }); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: { - pageType: contentPageContext.type, - pageLabelOrId: contentPageContext.id, - }, + it('should get cms page data by pageId if PageType is unknown', (done) => { + spyOn(endpointsService, 'buildUrl').and.returnValue( + userEndpoint + `/pages/${contextWithoutType.id}` + ); + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + service.load(contextWithoutType).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === userEndpoint + `/pages/${contextWithoutType.id}` + ); + }); + + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); + done(); + }); + }); + + describe('normalizer', () => { + it('should use normalizer', (done) => { + spyOn(userIdService, 'getUserId').and.returnValue(of('anonymous')); + spyOn(endpointsService, 'buildUrl').and.returnValue( + userEndpoint + '/pages' + ); + const converter = TestBed.inject(ConverterService); + + service.load(contentPageContext).subscribe(() => { + expect(converter.pipeable).toHaveBeenCalledWith(CMS_PAGE_NORMALIZER); + }); + + httpMock + .expectOne((req) => req.url === userEndpoint + '/pages') + .flush(cmsPageData); + done(); }); - expect(testRequest.cancelled).toBeFalsy(); - expect(testRequest.request.responseType).toEqual('json'); - testRequest.flush(cmsPageData); }); + }); - it('should get cms product page data', () => { - spyOn(endpointsService, 'buildUrl').and.returnValue( - endpoint + - `/pages?pageType=${productPageContext.type}&code=${productPageContext.id}` - ); - service.load(productPageContext).subscribe((result) => { - expect(result).toEqual(cmsPageData); + describe('default endpoints', () => { + beforeEach(() => { + spyOn(featureConfigService, 'isEnabled').and.returnValue(false); + }); + + describe('endpoint configuration', () => { + it('should get cms home page by specific context', () => { + spyOn(endpointsService, 'buildUrl'); + service.load(homePageContext); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: {}, + }); }); - const testRequest = httpMock.expectOne((req) => { - return ( - req.method === 'GET' && - req.url === - endpoint + - `/pages?pageType=${productPageContext.type}&code=${productPageContext.id}` - ); + it('should get cms pages by page type and id for any page', () => { + spyOn(endpointsService, 'buildUrl'); + service.load(contentPageContext); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageLabelOrId: contentPageContext.id, + pageType: PageType.CONTENT_PAGE, + }, + }); }); - expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { - queryParams: { - pageType: productPageContext.type, - code: productPageContext.id, - }, + it('should get cms product page by product code and ProductPage type', () => { + spyOn(endpointsService, 'buildUrl'); + service.load(productPageContext); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: PageType.PRODUCT_PAGE, + code: productPageContext.id, + }, + }); + }); + + it('should get cms page by pageId if there is no PageType', () => { + spyOn(endpointsService, 'buildUrl'); + service.load(contextWithoutType); + expect(endpointsService.buildUrl).toHaveBeenCalledWith('page', { + urlParams: { id: contextWithoutType.id }, + }); }); - expect(testRequest.cancelled).toBeFalsy(); - expect(testRequest.request.responseType).toEqual('json'); - testRequest.flush(cmsPageData); }); - it('should get cms page data by pageId if PageType is unknown', () => { - spyOn(endpointsService, 'buildUrl').and.returnValue( - endpoint + `/pages/${contextWithoutType.id}` - ); - service.load(contextWithoutType).subscribe((result) => { - expect(result).toEqual(cmsPageData); + describe('http', () => { + it('Should get home page', () => { + spyOn(endpointsService, 'buildUrl').and.returnValue( + endpoint + `/pages` + ); + + service.load(homePageContext).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return req.method === 'GET' && req.url === endpoint + `/pages`; + }); + + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: {}, + }); + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); }); - const testRequest = httpMock.expectOne((req) => { - return ( - req.method === 'GET' && - req.url === endpoint + `/pages/${contextWithoutType.id}` + it('Should get cms content page data', () => { + spyOn(endpointsService, 'buildUrl').and.returnValue( + endpoint + + `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` ); + + service.load(contentPageContext).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === + endpoint + + `/pages?pageType=${contentPageContext.type}&pageLabelOrId=${contentPageContext.id}` + ); + }); + + expect(endpointsService.buildUrl).toHaveBeenCalledWith('pages', { + queryParams: { + pageType: contentPageContext.type, + pageLabelOrId: contentPageContext.id, + }, + }); + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); }); - expect(testRequest.cancelled).toBeFalsy(); - expect(testRequest.request.responseType).toEqual('json'); - testRequest.flush(cmsPageData); + it('should get cms page data by pageId if PageType is unknown', () => { + spyOn(endpointsService, 'buildUrl').and.returnValue( + endpoint + `/pages/${contextWithoutType.id}` + ); + service.load(contextWithoutType).subscribe((result) => { + expect(result).toEqual(cmsPageData); + }); + + const testRequest = httpMock.expectOne((req) => { + return ( + req.method === 'GET' && + req.url === endpoint + `/pages/${contextWithoutType.id}` + ); + }); + + expect(testRequest.cancelled).toBeFalsy(); + expect(testRequest.request.responseType).toEqual('json'); + testRequest.flush(cmsPageData); + }); }); - }); - describe('normalizer', () => { - it('should use normalizer', () => { - spyOn(endpointsService, 'buildUrl').and.returnValue(endpoint + '/pages'); - const converter = TestBed.inject(ConverterService); + describe('normalizer', () => { + it('should use normalizer', () => { + spyOn(endpointsService, 'buildUrl').and.returnValue( + endpoint + '/pages' + ); + const converter = TestBed.inject(ConverterService); - service.load(contentPageContext).subscribe(); + service.load(contentPageContext).subscribe(); - httpMock - .expectOne((req) => req.url === endpoint + '/pages') - .flush(cmsPageData); + httpMock + .expectOne((req) => req.url === endpoint + '/pages') + .flush(cmsPageData); - expect(converter.pipeable).toHaveBeenCalledWith(CMS_PAGE_NORMALIZER); + expect(converter.pipeable).toHaveBeenCalledWith(CMS_PAGE_NORMALIZER); + }); }); }); }); diff --git a/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.ts b/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.ts index 11244d9be56..18887afae43 100644 --- a/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.ts +++ b/projects/core/src/occ/adapters/cms/occ-cms-page.adapter.ts @@ -5,12 +5,12 @@ */ import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { CmsPageAdapter } from '../../../cms/connectors/page/cms-page.adapter'; import { CMS_PAGE_NORMALIZER } from '../../../cms/connectors/page/converters'; import { CmsStructureModel } from '../../../cms/model/page.model'; -import { PageType } from '../../../model/cms.model'; +import { PageType, USER_CMS_ENDPOINTS } from '../../../model/cms.model'; import { HOME_PAGE_CONTEXT, PageContext, @@ -18,6 +18,9 @@ import { } from '../../../routing/models/page-context.model'; import { ConverterService } from '../../../util/converter.service'; import { OccEndpointsService } from '../../services/occ-endpoints.service'; +import { UserIdService } from '../../../auth'; +import { switchMap } from 'rxjs/operators'; +import { FeatureConfigService } from '../../../features-config'; export interface OccCmsPageRequest { pageLabelOrId?: string; @@ -30,6 +33,8 @@ export interface OccCmsPageRequest { providedIn: 'root', }) export class OccCmsPageAdapter implements CmsPageAdapter { + protected readonly userIdService = inject(UserIdService); + protected readonly featureConfigService = inject(FeatureConfigService); protected headers = new HttpHeaders().set('Content-Type', 'application/json'); constructor( @@ -44,13 +49,31 @@ export class OccCmsPageAdapter implements CmsPageAdapter { */ load(pageContext: PageContext): Observable { const params = this.getPagesRequestParams(pageContext); + // TODO: (CXSPA-4886) Remove flag in the major + if (this.featureConfigService.isEnabled(USER_CMS_ENDPOINTS)) { + return this.userIdService.getUserId().pipe( + switchMap((userId: string) => { + const endpoint = !pageContext.type + ? this.occEndpoints.buildUrl('page', { + urlParams: { id: pageContext.id, userId }, + }) + : this.occEndpoints.buildUrl('pages', { + urlParams: { userId }, + queryParams: params, + }); + return this.http.get(endpoint, { headers: this.headers }); + }), + this.converter.pipeable(CMS_PAGE_NORMALIZER) + ); + } const endpoint = !pageContext.type ? this.occEndpoints.buildUrl('page', { urlParams: { id: pageContext.id }, }) - : this.occEndpoints.buildUrl('pages', { queryParams: params }); - + : this.occEndpoints.buildUrl('pages', { + queryParams: params, + }); return this.http .get(endpoint, { headers: this.headers }) .pipe(this.converter.pipeable(CMS_PAGE_NORMALIZER));