Skip to content

Commit

Permalink
fix(uve): Retry request when returning unexpected null (#31087)
Browse files Browse the repository at this point in the history
This pull request includes several changes to the
`DotEmaBookmarksComponent` and related files to improve functionality
and testing. The most important changes include importing and using the
`mockCurrentUser`, adding retry logic for HTTP requests, and updating
the `fetchFavoritePage` method.

### Improvements to testing:
*
[`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-ema-bookmarks/dot-ema-bookmarks.component.spec.ts`](diffhunk://#diff-0f88ad4304e13721edc116c3da29e02ab5e79004a21b2cbdab6e9c57e7ca772dR21):
Imported `mockCurrentUser` and updated the `UVEStore` mock provider to
include `currentUser`.
[[1]](diffhunk://#diff-0f88ad4304e13721edc116c3da29e02ab5e79004a21b2cbdab6e9c57e7ca772dR21)
[[2]](diffhunk://#diff-0f88ad4304e13721edc116c3da29e02ab5e79004a21b2cbdab6e9c57e7ca772dL32-R36)

### Enhancements to HTTP request handling:
*
[`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/components/dot-ema-bookmarks/dot-ema-bookmarks.component.ts`](diffhunk://#diff-6cdfb5abade6eb8396ed84d8a8d520e262cc6edef0cdf48f908e150724f920abL1-L10):
Added `HttpClient` and `takeUntilDestroyed` imports, and included retry
logic in the `fetchFavoritePage` method.
[[1]](diffhunk://#diff-6cdfb5abade6eb8396ed84d8a8d520e262cc6edef0cdf48f908e150724f920abL1-L10)
[[2]](diffhunk://#diff-6cdfb5abade6eb8396ed84d8a8d520e262cc6edef0cdf48f908e150724f920abL27-R40)
[[3]](diffhunk://#diff-6cdfb5abade6eb8396ed84d8a8d520e262cc6edef0cdf48f908e150724f920abL71-R100)
*
[`core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/edit-ema-palette/store/edit-ema-palette.store.ts`](diffhunk://#diff-e60190c318aa61ddbc495b1c20398bb42e7baa8429b8241aa6964267f3ddd445L7-R7):
Added retry logic for HTTP requests in the `DotPaletteStore` class.
[[1]](diffhunk://#diff-e60190c318aa61ddbc495b1c20398bb42e7baa8429b8241aa6964267f3ddd445L7-R7)
[[2]](diffhunk://#diff-e60190c318aa61ddbc495b1c20398bb42e7baa8429b8241aa6964267f3ddd445R251-R259)

### Mock data updates:
*
[`core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts`](diffhunk://#diff-1da6b58a820756ad141b3669726954d3a57bc80960bd8cf746a6dbb4d6436a0dR3):
Added `mockCurrentUser` mock data.
[[1]](diffhunk://#diff-1da6b58a820756ad141b3669726954d3a57bc80960bd8cf746a6dbb4d6436a0dR3)
[[2]](diffhunk://#diff-1da6b58a820756ad141b3669726954d3a57bc80960bd8cf746a6dbb4d6436a0dR990-R998)

### Updates to existing tests:
*
[`core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.spec.ts`](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL8):
Replaced hardcoded `currentUser` with `mockCurrentUser` in test cases.
[[1]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL8)
[[2]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL17-R16)
[[3]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL48-L56)
[[4]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL196-R186)
[[5]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL216-R211)
[[6]](diffhunk://#diff-7a5de702ac1dc81304f4c31816f5c0363aa56141f8afa583a87856e7a0d8482dL244-R238)


## Screenshot


https://github.com/user-attachments/assets/8811bb7b-8bb2-47ca-9689-3353ca1a181b
  • Loading branch information
zJaaal authored Jan 9, 2025
1 parent ab6e3d7 commit bd9b25b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { LoginServiceMock, MockDotMessageService } from '@dotcms/utils-testing';

import { DotEmaBookmarksComponent } from './dot-ema-bookmarks.component';

import { mockCurrentUser } from '../../../../../shared/mocks';
import { UVEStore } from '../../../../../store/dot-uve.store';

describe('DotEmaBookmarksComponent', () => {
Expand All @@ -29,7 +30,10 @@ describe('DotEmaBookmarksComponent', () => {
providers: [
DialogService,
HttpClient,
mockProvider(UVEStore, { $previewMode: signal(false) }),
mockProvider(UVEStore, {
$previewMode: signal(false),
currentUser: signal(mockCurrentUser)
}),
{
provide: LoginService,
useClass: LoginServiceMock
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { ChangeDetectionStrategy, Component, Input, OnInit, inject, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {
ChangeDetectionStrategy,
Component,
DestroyRef,
Input,
OnInit,
inject,
signal
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

import { ButtonModule } from 'primeng/button';
import { DialogService } from 'primeng/dynamicdialog';
import { TooltipModule } from 'primeng/tooltip';

import { map, switchMap } from 'rxjs/operators';
import { delay, map, retryWhen, takeWhile } from 'rxjs/operators';

import { DotFavoritePageService, DotMessageService } from '@dotcms/data-access';
import { LoginService } from '@dotcms/dotcms-js';
import { DotCMSContentlet } from '@dotcms/dotcms-models';
import { DotFavoritePageComponent } from '@dotcms/portlets/dot-ema/ui';
import { DotMessagePipe } from '@dotcms/ui';
Expand All @@ -24,10 +33,11 @@ import { UVEStore } from '../../../../../store/dot-uve.store';
export class DotEmaBookmarksComponent implements OnInit {
@Input() url = '';

private readonly loginService = inject(LoginService);
private readonly dotFavoritePageService = inject(DotFavoritePageService);
private readonly http = inject(HttpClient);
private readonly dialogService = inject(DialogService);
private readonly dotMessageService = inject(DotMessageService);
private readonly destroyRef = inject(DestroyRef);
protected readonly store = inject(UVEStore);

favoritePage: DotCMSContentlet;
Expand Down Expand Up @@ -68,18 +78,25 @@ export class DotEmaBookmarksComponent implements OnInit {
private fetchFavoritePage(url: string): void {
this.loading.set(true);

this.loginService
.getCurrentUser()
this.dotFavoritePageService
.get({
url,
userId: this.store.currentUser()?.userId,
limit: 10
})
.pipe(
switchMap((user) => {
return this.dotFavoritePageService
.get({
url,
userId: user.userId,
limit: 10
takeUntilDestroyed(this.destroyRef),
retryWhen((errors) =>
errors.pipe(
delay(500),
takeWhile((error) => {
// This request is returning null in some cases and we need to retry
return error instanceof TypeError;
})
.pipe(map((res) => res.jsonObjectView.contentlets[0]));
})
)
),

map((res) => res.jsonObjectView.contentlets[0])
)
.subscribe((favoritePage) => {
this.loading.set(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { EMPTY, Observable, forkJoin } from 'rxjs';

import { Injectable, inject } from '@angular/core';

import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { catchError, delay, map, retryWhen, switchMap, takeWhile, tap } from 'rxjs/operators';

import {
DotContentTypeService,
Expand Down Expand Up @@ -248,6 +248,15 @@ export class DotPaletteStore extends ComponentStore<DotPaletteState> {
DotConfigurationVariables.CONTENT_PALETTE_HIDDEN_CONTENT_TYPES
)
}).pipe(
retryWhen((errors) =>
errors.pipe(
delay(500),
takeWhile((error) => {
// The request is returning null in some cases and we need to retry
return error instanceof TypeError;
})
)
),
map(({ contentTypes, widgets, hiddenContentTypes }) => {
/**
* This filter is used to prevent widgets from being repeated.
Expand Down
10 changes: 10 additions & 0 deletions core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { of } from 'rxjs';

import { CurrentUser } from '@dotcms/dotcms-js';
import {
DEFAULT_VARIANT_ID,
DotPageContainerStructure,
Expand Down Expand Up @@ -986,3 +987,12 @@ export const dotPropertiesServiceMock = {
[FeaturedFlags.FEATURE_FLAG_UVE_PREVIEW_MODE]: false
})
};

export const mockCurrentUser: CurrentUser = {
email: '[email protected]',
givenName: 'Test',
loginAs: false,
roleId: 'role123',
surname: 'User',
userId: 'user123'
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { of } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';

import { UVE_MODE } from '@dotcms/client';
import { CurrentUser } from '@dotcms/dotcms-js';
import { DEFAULT_VARIANT_ID, DEFAULT_VARIANT_NAME } from '@dotcms/dotcms-models';
import { getRunningExperimentMock, mockDotDevices } from '@dotcms/utils-testing';

Expand All @@ -15,7 +14,7 @@ import { withUVEToolbar } from './withUVEToolbar';
import { DotPageApiService } from '../../../../services/dot-page-api.service';
import { DEFAULT_PERSONA } from '../../../../shared/consts';
import { UVE_STATUS } from '../../../../shared/enums';
import { MOCK_RESPONSE_HEADLESS } from '../../../../shared/mocks';
import { MOCK_RESPONSE_HEADLESS, mockCurrentUser } from '../../../../shared/mocks';
import { Orientation, UVEState } from '../../../models';

const pageParams = {
Expand Down Expand Up @@ -46,15 +45,6 @@ const initialState: UVEState = {
}
};

const currentUser: CurrentUser = {
email: '[email protected]',
givenName: 'Test',
loginAs: false,
roleId: 'role123',
surname: 'User',
userId: 'user123'
};

export const uveStoreMock = signalStore(
{ protectedState: false },
withState<UVEState>(initialState),
Expand Down Expand Up @@ -194,7 +184,7 @@ describe('withEditor', () => {
lockedBy: '456'
}
},
currentUser
currentUser: mockCurrentUser
});
expect(store.$infoDisplayProps()).toEqual({
icon: 'pi pi-lock',
Expand All @@ -214,12 +204,12 @@ describe('withEditor', () => {
...MOCK_RESPONSE_HEADLESS.page,
locked: true,
canLock: false,
lockedByName: currentUser.givenName,
lockedBy: currentUser.userId
lockedByName: mockCurrentUser.givenName,
lockedBy: mockCurrentUser.userId
}
},
currentUser: {
...currentUser,
...mockCurrentUser,
userId: '123'
}
});
Expand All @@ -242,11 +232,11 @@ describe('withEditor', () => {
...MOCK_RESPONSE_HEADLESS.page,
locked: true,
canLock: true,
lockedByName: currentUser.givenName,
lockedBy: currentUser.userId
lockedByName: mockCurrentUser.givenName,
lockedBy: mockCurrentUser.userId
}
},
currentUser
currentUser: mockCurrentUser
});

expect(store.$infoDisplayProps()).toBe(null);
Expand Down

0 comments on commit bd9b25b

Please sign in to comment.