Skip to content

Commit 1d4c472

Browse files
authored
33073 rxjs from v6 to v7 (#35145)
This pull request focuses on migrating the codebase away from deprecated RxJS 6 patterns to be fully compatible with RxJS 7.8.2. The changes are primarily aimed at replacing deprecated operators like `pluck`, `switchMapTo`, and `publishLast` with their recommended alternatives, as well as updating test code to align with newer RxJS error handling APIs. The refactor touches a wide range of files, particularly in the `dotcms-ui` app, and ensures improved maintainability and future compatibility. **RxJS Operator Refactoring:** * Replaced deprecated `pluck` operator with `map` across all usages in the codebase, updating selector logic accordingly [[1]](diffhunk://#diff-6c19b151b96d1e9277930b34178414a4bb8edb3727bf54efb078f67722d91679L6-R6) [[2]](diffhunk://#diff-6c19b151b96d1e9277930b34178414a4bb8edb3727bf54efb078f67722d91679L42-R47) [[3]](diffhunk://#diff-a38219f03b97c41ff2b3e3e6fda3171d8deed438c9c1d5f933c30d3dd58fbb32L21-R21) [[4]](diffhunk://#diff-a38219f03b97c41ff2b3e3e6fda3171d8deed438c9c1d5f933c30d3dd58fbb32L84-R89) [[5]](diffhunk://#diff-033a32c658da882cbc2420b75ca50850898c3f1e6c722bb2add6522da75c6349L8-R8) [[6]](diffhunk://#diff-033a32c658da882cbc2420b75ca50850898c3f1e6c722bb2add6522da75c6349L82-R85) [[7]](diffhunk://#diff-188a97cb6de981d161e466dfc422052b39ac67ec5c4753ef54db7065442ef430L6-R6) [[8]](diffhunk://#diff-188a97cb6de981d161e466dfc422052b39ac67ec5c4753ef54db7065442ef430L41-R44) [[9]](diffhunk://#diff-ab22dfefdad25b6f40208fe418538738c29327375fbb37072ba18d098effa330L8-R8) [[10]](diffhunk://#diff-ab22dfefdad25b6f40208fe418538738c29327375fbb37072ba18d098effa330L64-R64) [[11]](diffhunk://#diff-f737fd3bdbe5670f34ce3ee6a82c427a5460835c6222b86527b9f3aa6e17a044L7-R7) [[12]](diffhunk://#diff-f737fd3bdbe5670f34ce3ee6a82c427a5460835c6222b86527b9f3aa6e17a044L25-R25). * Replaced deprecated `switchMapTo` with `switchMap` in all affected streams, updating function signatures where necessary [[1]](diffhunk://#diff-b202dfd3e850f6f8d29a81e0f698f305309a1d524fcb089138c0eed4f0b0e2d0L9-R9) [[2]](diffhunk://#diff-b202dfd3e850f6f8d29a81e0f698f305309a1d524fcb089138c0eed4f0b0e2d0L170-R170) [[3]](diffhunk://#diff-b202dfd3e850f6f8d29a81e0f698f305309a1d524fcb089138c0eed4f0b0e2d0L191-R191). * Replaced deprecated `publishLast`/`refCount` combo with `shareReplay` for caching HTTP responses [[1]](diffhunk://#diff-6d1086b6ba6f8165deecbdc80ac9408a311b0b72781c89d3979b225a226f046aL6-R6) [[2]](diffhunk://#diff-6d1086b6ba6f8165deecbdc80ac9408a311b0b72781c89d3979b225a226f046aL72-R66). * Updated usages of `pluck` in service methods to `map`, ensuring type safety and clarity. **Test and Error Handling Updates:** * Updated all usages of `throwError(error)` to the RxJS 7+ pattern `throwError(() => error)` in tests and services for proper error emission [[1]](diffhunk://#diff-cb2b54d67fe7a39ef735fb2a985b7202ba8e25b653e456eeed74ada2bfc1ddbaL107-R107) [[2]](diffhunk://#diff-c0762a9384069a32c029261a27d2ab7ae8bce5eea7850d8188a93f0cd8502341L158-R158) [[3]](diffhunk://#diff-c0762a9384069a32c029261a27d2ab7ae8bce5eea7850d8188a93f0cd8502341L181-R183) [[4]](diffhunk://#diff-c0762a9384069a32c029261a27d2ab7ae8bce5eea7850d8188a93f0cd8502341L202-R204) [[5]](diffhunk://#diff-56b35eae311934a1279fbbf6999050daa4da884729bc6cac65890f20b2e61602L320-R320). **Testing Utilities Modernization:** * Replaced deprecated `fakeAsync`/`tick` usage with Jest timer mocks (`jest.useFakeTimers`, `jest.advanceTimersByTime`, `jest.useRealTimers`) in component tests for more robust async testing [[1]](diffhunk://#diff-3bdd1a7f98eb62dd492e069085d19306cc031c98605e1e67f6f9e017494d9ba3L5-L6) [[2]](diffhunk://#diff-3bdd1a7f98eb62dd492e069085d19306cc031c98605e1e67f6f9e017494d9ba3R62) [[3]](diffhunk://#diff-3bdd1a7f98eb62dd492e069085d19306cc031c98605e1e67f6f9e017494d9ba3R85) [[4]](diffhunk://#diff-3bdd1a7f98eb62dd492e069085d19306cc031c98605e1e67f6f9e017494d9ba3L107-R112). **Documentation:** * Added `.cursor/plans/rxjs_7_migration_plan_c65e7afc.plan.md` to document the migration plan and provide an overview of the categories and scope of changes. This PR fixes: #33073
1 parent b085255 commit 1d4c472

128 files changed

Lines changed: 828 additions & 692 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core-web/apps/dotcdn/src/app/dotcdn.component.store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { inject, Injectable } from '@angular/core';
66

77
import { SelectItem } from 'primeng/api';
88

9-
import { mergeMap, switchMapTo, tap } from 'rxjs/operators';
9+
import { mergeMap, switchMap, tap } from 'rxjs/operators';
1010

1111
import {
1212
ChartData,
@@ -167,7 +167,7 @@ export class DotCDNStore extends ComponentStore<DotCDNState> {
167167
);
168168

169169
return loading$.pipe(
170-
switchMapTo(
170+
switchMap(() =>
171171
this.dotCdnService.purgeCache(urls).pipe(
172172
tap(() => {
173173
this.dispatchLoading({
@@ -188,7 +188,7 @@ export class DotCDNStore extends ComponentStore<DotCDNState> {
188188
})
189189
);
190190

191-
$loading.pipe(switchMapTo(this.dotCdnService.purgeCacheAll())).subscribe(() => {
191+
$loading.pipe(switchMap(() => this.dotCdnService.purgeCacheAll())).subscribe(() => {
192192
this.dispatchLoading({
193193
loadingState: LoadingState.LOADED,
194194
loader: Loader.PURGE_PULL_ZONE

core-web/apps/dotcms-ui/src/app/api/services/dot-menu.service.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,7 @@ import { Observable } from 'rxjs';
33
import { HttpClient } from '@angular/common/http';
44
import { Injectable, inject } from '@angular/core';
55

6-
import {
7-
defaultIfEmpty,
8-
filter,
9-
find,
10-
map,
11-
mergeMap,
12-
pluck,
13-
publishLast,
14-
refCount
15-
} from 'rxjs/operators';
6+
import { defaultIfEmpty, filter, find, map, mergeMap, shareReplay } from 'rxjs/operators';
167

178
import { DotCMSResponse, DotMenu, DotMenuItem } from '@dotcms/dotcms-models';
189

@@ -33,7 +24,7 @@ export class DotMenuService {
3324
getUrlById(id: string): Observable<string> {
3425
return this.getMenuItems().pipe(
3526
filter((res: DotMenuItem) => !res.angular && res.id === id),
36-
pluck('url'),
27+
map((res) => res?.url),
3728
defaultIfEmpty('')
3829
);
3930
}
@@ -69,9 +60,10 @@ export class DotMenuService {
6960
*/
7061
loadMenu(force = false): Observable<DotMenu[]> {
7162
if (!this.menu$ || force) {
72-
this.menu$ = this.http
73-
.get<DotCMSResponse<DotMenu>>(this.urlMenus)
74-
.pipe(publishLast(), refCount(), pluck('entity'));
63+
this.menu$ = this.http.get<DotCMSResponse<DotMenu[]>>(this.urlMenus).pipe(
64+
shareReplay({ bufferSize: 1, refCount: true }),
65+
map((x) => x.entity)
66+
);
7567
}
7668

7769
return this.menu$;

core-web/apps/dotcms-ui/src/app/api/services/dynamic-route-initializer.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('DynamicRouteInitializerService', () => {
104104
});
105105

106106
it('should resolve to 0 on error', async () => {
107-
(menuService.loadMenu as jest.Mock).mockReturnValue(throwError(new Error('fail')));
107+
(menuService.loadMenu as jest.Mock).mockReturnValue(throwError(() => new Error('fail')));
108108

109109
const count = await service.initialize();
110110

core-web/apps/dotcms-ui/src/app/app.component.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ describe('AppComponent', () => {
155155

156156
it('should use default colors when configuration fails to load', () => {
157157
const error = new Error('Failed to load configuration');
158-
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(throwError(error));
158+
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(throwError(() => error));
159159

160160
fixture.detectChanges();
161161

@@ -178,7 +178,9 @@ describe('AppComponent', () => {
178178

179179
it('should handle configuration error gracefully (unauthenticated user)', () => {
180180
const httpError = { status: 401, message: 'Unauthorized' };
181-
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(throwError(httpError));
181+
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
182+
throwError(() => httpError)
183+
);
182184

183185
fixture.detectChanges();
184186

@@ -199,7 +201,7 @@ describe('AppComponent', () => {
199201

200202
it('should always set colors even when configuration fails', () => {
201203
jest.spyOn(dotCmsConfigService, 'getConfig').mockReturnValue(
202-
throwError(new Error('Network error'))
204+
throwError(() => new Error('Network error'))
203205
);
204206

205207
// Mock querySelector to return null (edge case)

core-web/apps/dotcms-ui/src/app/portlets/dot-apps/components/dot-apps-configuration-detail/dot-apps-configuration-detail.component.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
33

44
import { ButtonModule } from 'primeng/button';
55

6-
import { pluck, take } from 'rxjs/operators';
6+
import { map, take } from 'rxjs/operators';
77

88
import { DotAppsService, DotRouterService } from '@dotcms/data-access';
99
import { DotApp, DotAppsSaveData, DotAppsSecret } from '@dotcms/dotcms-models';
@@ -39,13 +39,18 @@ export class DotAppsConfigurationDetailComponent implements OnInit {
3939
formValid = false;
4040

4141
ngOnInit() {
42-
this.route.data.pipe(pluck('data'), take(1)).subscribe((app: DotApp) => {
43-
this.apps = app;
44-
this.formFields = this.getSecrets(app.sites[0].secrets);
45-
this.dynamicVariables = this.transformSecretsToKeyValue(
46-
this.getSecrets(app.sites[0].secrets, true)
47-
);
48-
});
42+
this.route.data
43+
.pipe(
44+
map((x) => x?.data),
45+
take(1)
46+
)
47+
.subscribe((app: DotApp) => {
48+
this.apps = app;
49+
this.formFields = this.getSecrets(app.sites[0].secrets);
50+
this.dynamicVariables = this.transformSecretsToKeyValue(
51+
this.getSecrets(app.sites[0].secrets, true)
52+
);
53+
});
4954
}
5055

5156
/**

core-web/apps/dotcms-ui/src/app/portlets/dot-apps/components/dot-apps-configuration/dot-apps-configuration.component.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { LazyLoadEvent } from 'primeng/api';
1818
import { ButtonModule } from 'primeng/button';
1919
import { InputTextModule } from 'primeng/inputtext';
2020

21-
import { debounceTime, pluck, take } from 'rxjs/operators';
21+
import { debounceTime, map, take } from 'rxjs/operators';
2222

2323
import {
2424
DotAlertConfirmService,
@@ -81,22 +81,27 @@ export class DotAppsConfigurationComponent implements OnInit, AfterViewInit {
8181
});
8282

8383
ngOnInit() {
84-
this.#route.data.pipe(pluck('data'), take(1)).subscribe((app: DotApp) => {
85-
patchState(this.$state, {
86-
app: {
87-
...app,
88-
sites: []
89-
}
90-
});
84+
this.#route.data
85+
.pipe(
86+
map((x) => x?.data),
87+
take(1)
88+
)
89+
.subscribe((app: DotApp) => {
90+
patchState(this.$state, {
91+
app: {
92+
...app,
93+
sites: []
94+
}
95+
});
9196

92-
// Initialize pagination after app data is available
93-
this.paginationService.url = `v1/apps/${app.key}`;
94-
this.paginationService.paginationPerPage = this.$paginationPerPage();
95-
this.paginationService.sortField = 'name';
96-
this.paginationService.setExtraParams('filter', '');
97-
this.paginationService.sortOrder = 1;
98-
this.loadData();
99-
});
97+
// Initialize pagination after app data is available
98+
this.paginationService.url = `v1/apps/${app.key}`;
99+
this.paginationService.paginationPerPage = this.$paginationPerPage();
100+
this.paginationService.sortField = 'name';
101+
this.paginationService.setExtraParams('filter', '');
102+
this.paginationService.sortOrder = 1;
103+
this.loadData();
104+
});
100105
}
101106

102107
ngAfterViewInit() {

core-web/apps/dotcms-ui/src/app/portlets/dot-apps/dot-apps-import-export-dialog/store/dot-apps-import-export-dialog.store.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ describe('DotAppsImportExportDialogStore', () => {
317317
it('should set error on import error', () => {
318318
spectator.service.openImport();
319319
dotAppsService.importConfiguration.mockReturnValue(
320-
throwError(new Error('Network error'))
320+
throwError(() => new Error('Network error'))
321321
);
322322

323323
spectator.service.importConfiguration({

core-web/apps/dotcms-ui/src/app/portlets/dot-containers/container-list/store/dot-container-list.store.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ActivatedRoute } from '@angular/router';
55

66
import { MenuItem } from 'primeng/api';
77

8-
import { pluck, switchMap, take, tap } from 'rxjs/operators';
8+
import { map, switchMap, take, tap } from 'rxjs/operators';
99

1010
import {
1111
DotAlertConfirmService,
@@ -79,7 +79,10 @@ export class DotContainerListStore extends ComponentStore<DotContainerListState>
7979

8080
this.paginatorService.setExtraParams('host', identifier);
8181

82-
return this.route.data.pipe(pluck('dotContainerListResolverData'), take(1));
82+
return this.route.data.pipe(
83+
map((x) => x?.dotContainerListResolverData),
84+
take(1)
85+
);
8386
})
8487
)
8588
.subscribe(([isEnterprise, hasEnvironments]: [boolean, boolean]) => {

core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-create.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router';
33

44
import { TabsModule } from 'primeng/tabs';
55

6-
import { pluck, take } from 'rxjs/operators';
6+
import { map, take } from 'rxjs/operators';
77

88
import { DotRouterService } from '@dotcms/data-access';
99
import { DotContainerEntity } from '@dotcms/dotcms-models';
@@ -38,7 +38,10 @@ export class DotContainerCreateComponent implements OnInit {
3838

3939
ngOnInit() {
4040
this.activatedRoute.data
41-
.pipe(pluck('container'), take(1))
41+
.pipe(
42+
map((x) => x?.container),
43+
take(1)
44+
)
4245
.subscribe((container: DotContainerEntity) => {
4346
if (container?.container) this.containerId = container.container.identifier;
4447
else this.dotRouterService.goToCreateContainer();

core-web/apps/dotcms-ui/src/app/portlets/dot-containers/dot-container-create/dot-container-properties/store/dot-container-properties.store.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { HttpErrorResponse } from '@angular/common/http';
55
import { Injectable, inject } from '@angular/core';
66
import { ActivatedRoute } from '@angular/router';
77

8-
import { catchError, filter, pluck, switchMap, take, tap } from 'rxjs/operators';
8+
import { catchError, filter, map, switchMap, take, tap } from 'rxjs/operators';
99

1010
import {
1111
DotContentTypeService,
@@ -61,7 +61,7 @@ export class DotContainerPropertiesStore extends ComponentStore<DotContainerProp
6161
});
6262
this.activatedRoute.data
6363
.pipe(
64-
pluck('container'),
64+
map((x) => x?.container),
6565
take(1),
6666
filter((containerEntity) => !!containerEntity)
6767
)

0 commit comments

Comments
 (0)