Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
88099c4
refactor: replace CoreWebService with HttpClient in various services
nicobytes Dec 24, 2025
c75ce51
refactor: enhance DotAccountService and update imports in various ser…
nicobytes Dec 24, 2025
2dda237
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 24, 2025
0d79e82
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 26, 2025
acf212d
refactor: replace request method with HttpClient in DotContainersServ…
nicobytes Dec 26, 2025
3a030f9
refactor: update tests and services to use Spectator and HttpClient f…
nicobytes Dec 26, 2025
56ab6f8
refactor: streamline test setup by replacing HttpClientTestingModule …
nicobytes Dec 26, 2025
8e2bdc5
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 26, 2025
2ead251
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 29, 2025
d95898f
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 30, 2025
4c7b2a7
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 30, 2025
ecc5d67
Merge branch 'main' into 33073-migrate-core-web
nicobytes Dec 31, 2025
06975ee
sync with master.
nicobytes Jan 5, 2026
3572a25
Merge branch '33073-migrate-core-web' of github.com:dotCMS/core into …
nicobytes Jan 5, 2026
42c6b37
Merge branch 'main' into 33073-migrate-core-web
nicobytes Jan 5, 2026
31aca1d
Merge branch 'main' into 33073-migrate-core-web
nicobytes Jan 5, 2026
44bc225
Merge branch 'main' into 33073-migrate-core-web
nicobytes Jan 9, 2026
239d243
Merge branch 'main' into 33073-migrate-core-web
nicobytes Jan 16, 2026
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
392 changes: 119 additions & 273 deletions core-web/apps/dotcdn/src/app/dotcdn.service.spec.ts

Large diffs are not rendered by default.

44 changes: 25 additions & 19 deletions core-web/apps/dotcdn/src/app/dotcdn.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { format, subDays } from 'date-fns';
import { Observable } from 'rxjs';

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

import { mergeMap, pluck } from 'rxjs/operators';
import { mergeMap, map } from 'rxjs/operators';

import { CoreWebService, ResponseView, SiteService } from '@dotcms/dotcms-js';
import { SiteService } from '@dotcms/dotcms-js';
import { DotCMSResponse } from '@dotcms/dotcms-models';

import { DotCDNStats, PurgeReturnData, PurgeUrlOptions } from './app.models';

// Response type for endpoints that return bodyJsonObject
interface DotBodyJsonResponse<T> {
bodyJsonObject: T;
}

@Injectable({
providedIn: 'root'
})
export class DotCDNService {
private coreWebService = inject(CoreWebService);
private http = inject(HttpClient);
private siteService = inject(SiteService);

/**
Expand All @@ -25,58 +32,57 @@ export class DotCDNService {
*/
requestStats(period: string): Observable<DotCDNStats> {
return this.siteService.getCurrentSite().pipe(
pluck('identifier'),
map((site) => site.identifier),
mergeMap((hostId: string) => {
const dateTo = format(new Date(), 'yyyy-MM-dd');
const dateFrom = format(subDays(new Date(), parseInt(period, 10)), 'yyyy-MM-dd');

return this.coreWebService.requestView<DotCDNStats>({
url: `/api/v1/dotcdn/stats?hostId=${hostId}&dateFrom=${dateFrom}&dateTo=${dateTo}`
});
}),
pluck('entity')
return this.http
.get<
DotCMSResponse<DotCDNStats>
>(`/api/v1/dotcdn/stats?hostId=${hostId}&dateFrom=${dateFrom}&dateTo=${dateTo}`)
.pipe(map((response) => response.entity));
})
);
}

/**
* Makes a request to purge the cache
*
* @param {string[]} [urls=[]]
* @return {Observable<ResponseView<PurgeReturnData>>}
* @return {Observable<PurgeReturnData>}
* @memberof DotCDNService
*/
purgeCache(urls?: string[]): Observable<PurgeReturnData> {
return this.siteService.getCurrentSite().pipe(
pluck('identifier'),
map((site) => site.identifier),
mergeMap((hostId: string) => {
return this.purgeUrlRequest({ hostId, invalidateAll: false, urls });
}),
pluck('bodyJsonObject')
map((response) => response.bodyJsonObject)
);
}

/**
* Makes a request to purge the cache
*
* @return {Observable<ResponseView<PurgeReturnData>>}
* @return {Observable<PurgeReturnData>}
* @memberof DotCDNService
*/
purgeCacheAll(): Observable<PurgeReturnData> {
return this.siteService.getCurrentSite().pipe(
pluck('identifier'),
map((site) => site.identifier),
mergeMap((hostId: string) => this.purgeUrlRequest({ hostId, invalidateAll: true })),
pluck('bodyJsonObject')
map((response) => response.bodyJsonObject)
);
}

private purgeUrlRequest({
urls = [],
invalidateAll,
hostId
}: PurgeUrlOptions): Observable<ResponseView<PurgeReturnData>> {
return this.coreWebService.requestView<PurgeReturnData>({
url: `/api/v1/dotcdn`,
method: 'DELETE',
}: PurgeUrlOptions): Observable<DotBodyJsonResponse<PurgeReturnData>> {
return this.http.request<DotBodyJsonResponse<PurgeReturnData>>('DELETE', '/api/v1/dotcdn', {
body: JSON.stringify({
urls,
invalidateAll,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import { mockProvider } from '@ngneat/spectator/jest';
import { throwError } from 'rxjs';
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { getTestBed, TestBed } from '@angular/core/testing';
import { DotHttpErrorManagerService } from '@dotcms/data-access';
import { MockDotHttpErrorManagerService } from '@dotcms/utils-testing';

import { ConfirmationService } from 'primeng/api';

import {
DotHttpErrorManagerService,
DotMessageDisplayService,
DotRouterService,
DotAlertConfirmService,
DotMessageService,
DotFormatDateService
} from '@dotcms/data-access';
import { CoreWebService, LoginService } from '@dotcms/dotcms-js';
import {
CoreWebServiceMock,
LoginServiceMock,
DotMessageDisplayServiceMock,
MockDotRouterService,
DotFormatDateServiceMock,
mockResponseView
} from '@dotcms/utils-testing';

import { DotAddToMenuService, DotCreateCustomTool } from './add-to-menu.service';
DotAddToMenuService,
DotCreateCustomTool,
DotCustomToolToLayout
} from './add-to-menu.service';

const customToolData: DotCreateCustomTool = {
contentTypes: 'Blog',
Expand All @@ -33,39 +18,30 @@ const customToolData: DotCreateCustomTool = {
};

describe('DotAddToMenuService', () => {
let injector: TestBed;
let dotAddToMenuService: DotAddToMenuService;
let dotHttpErrorManagerService: DotHttpErrorManagerService;
let coreWebService: CoreWebService;
let httpMock: HttpTestingController;
let httpTesting: HttpTestingController;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{ provide: CoreWebService, useClass: CoreWebServiceMock },
{
provide: LoginService,
useClass: LoginServiceMock
},
{
provide: DotMessageDisplayService,
useClass: DotMessageDisplayServiceMock
},
{ provide: DotRouterService, useClass: MockDotRouterService },
{ provide: DotFormatDateService, useClass: DotFormatDateServiceMock },
ConfirmationService,
provideHttpClient(),
provideHttpClientTesting(),
DotAddToMenuService,
DotAlertConfirmService,
DotHttpErrorManagerService,
mockProvider(DotMessageService)
{
provide: DotHttpErrorManagerService,
useClass: MockDotHttpErrorManagerService
}
]
});
injector = getTestBed();
dotAddToMenuService = injector.inject(DotAddToMenuService);
dotHttpErrorManagerService = injector.inject(DotHttpErrorManagerService);
coreWebService = injector.inject(CoreWebService);
httpMock = injector.inject(HttpTestingController);

dotAddToMenuService = TestBed.inject(DotAddToMenuService);
dotHttpErrorManagerService = TestBed.inject(DotHttpErrorManagerService);
httpTesting = TestBed.inject(HttpTestingController);
});

afterEach(() => {
httpTesting.verify();
});

it('should clean up Portlet Id value', () => {
Expand All @@ -75,13 +51,11 @@ describe('DotAddToMenuService', () => {
});

it('should create a custom tool portlet', () => {
const url = `v1/portlet/custom`;

dotAddToMenuService.createCustomTool(customToolData).subscribe((response: string) => {
expect(response).toEqual('ok');
});

const req = httpMock.expectOne(url);
const req = httpTesting.expectOne('/api/v1/portlet/custom');
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual({
...customToolData,
Expand All @@ -93,65 +67,69 @@ describe('DotAddToMenuService', () => {
});

it('should throw null on create custom tool error 400', () => {
const error404 = mockResponseView(400);
jest.spyOn(dotHttpErrorManagerService, 'handle');
jest.spyOn(coreWebService, 'requestView').mockReturnValue(throwError(error404));

dotAddToMenuService.createCustomTool(customToolData).subscribe((response: string) => {
expect(response).toEqual(null);
});

const req = httpTesting.expectOne('/api/v1/portlet/custom');
req.flush(null, { status: 400, statusText: 'Bad Request' });

expect(dotHttpErrorManagerService.handle).not.toHaveBeenCalled();
});

it('should throw error 500 on create custom tool error', () => {
const error404 = mockResponseView(500);
jest.spyOn(dotHttpErrorManagerService, 'handle');
jest.spyOn(coreWebService, 'requestView').mockReturnValue(throwError(error404));

dotAddToMenuService.createCustomTool(customToolData).subscribe((response: string) => {
expect(response).toEqual(null);
});
expect(dotHttpErrorManagerService.handle).toHaveBeenCalledWith(mockResponseView(500));

const req = httpTesting.expectOne('/api/v1/portlet/custom');
req.flush(null, { status: 500, statusText: 'Internal Server Error' });

expect(dotHttpErrorManagerService.handle).toHaveBeenCalled();
});

it('should add to layout a custom tool portlet', () => {
const url = `v1/portlet/custom/c_${customToolData.portletName}_${customToolData.dataViewMode}/_addtolayout/123`;

dotAddToMenuService
.addToLayout({
portletName: customToolData.portletName,
dataViewMode: customToolData.dataViewMode,
layoutId: '123'
})
.subscribe((response: string) => {
expect(response).toEqual('ok');
});

const req = httpMock.expectOne(url);
const layoutData: DotCustomToolToLayout = {
portletName: customToolData.portletName,
dataViewMode: customToolData.dataViewMode,
layoutId: '123'
};

dotAddToMenuService.addToLayout(layoutData).subscribe((response: string) => {
expect(response).toEqual('ok');
});

const req = httpTesting.expectOne(
`/api/v1/portlet/custom/c_${customToolData.portletName}_${customToolData.dataViewMode}/_addtolayout/123`
);
expect(req.request.method).toBe('PUT');
req.flush({
entity: 'ok'
});
});

it('should throw error 400 on add to layout custom portlet', () => {
const error404 = mockResponseView(400);
jest.spyOn(dotHttpErrorManagerService, 'handle');
jest.spyOn(coreWebService, 'requestView').mockReturnValue(throwError(error404));

dotAddToMenuService
.addToLayout({
portletName: customToolData.portletName,
dataViewMode: customToolData.dataViewMode,
layoutId: '123'
})
.subscribe((response: string) => {
expect(response).toEqual(null);
});
expect(dotHttpErrorManagerService.handle).toHaveBeenCalledWith(mockResponseView(400));
});

afterEach(() => {
httpMock.verify();
const layoutData: DotCustomToolToLayout = {
portletName: customToolData.portletName,
dataViewMode: customToolData.dataViewMode,
layoutId: '123'
};

dotAddToMenuService.addToLayout(layoutData).subscribe((response: string) => {
expect(response).toEqual(null);
});

const req = httpTesting.expectOne(
`/api/v1/portlet/custom/c_${customToolData.portletName}_${customToolData.dataViewMode}/_addtolayout/123`
);
req.flush(null, { status: 400, statusText: 'Bad Request' });

expect(dotHttpErrorManagerService.handle).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Observable, of } from 'rxjs';

import { HttpErrorResponse } from '@angular/common/http';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';

import { catchError, map, pluck, take } from 'rxjs/operators';
import { catchError, map, take } from 'rxjs/operators';

import { DotHttpErrorManagerService } from '@dotcms/data-access';
import { CoreWebService } from '@dotcms/dotcms-js';
import { DotCMSResponse } from '@dotcms/dotcms-models';

const addToMenuUrl = `v1/portlet`;
const addToMenuUrl = '/api/v1/portlet';

export interface DotCreateCustomTool {
contentTypes: string;
Expand All @@ -29,7 +29,7 @@ export interface DotCustomToolToLayout {
*/
@Injectable()
export class DotAddToMenuService {
private coreWebService = inject(CoreWebService);
private http = inject(HttpClient);
private httpErrorManagerService = inject(DotHttpErrorManagerService);

/**
Expand All @@ -51,17 +51,13 @@ export class DotAddToMenuService {
* @memberof DotAddToMenuService
*/
createCustomTool(params: DotCreateCustomTool): Observable<string> {
return this.coreWebService
.requestView({
body: {
...params,
portletId: `${this.cleanUpPorletId(params.portletName)}_${params.dataViewMode}`
},
method: 'POST',
url: `${addToMenuUrl}/custom`
return this.http
.post<DotCMSResponse<string>>(`${addToMenuUrl}/custom`, {
...params,
portletId: `${this.cleanUpPorletId(params.portletName)}_${params.dataViewMode}`
})
.pipe(
pluck('entity'),
map((response) => response.entity),
catchError((error: HttpErrorResponse) => {
if (error.status === 400) {
return of(null);
Expand All @@ -85,13 +81,12 @@ export class DotAddToMenuService {
addToLayout(params: DotCustomToolToLayout): Observable<string> {
const portletId = `${this.cleanUpPorletId(params.portletName)}_${params.dataViewMode}`;

return this.coreWebService
.requestView({
method: 'PUT',
url: `${addToMenuUrl}/custom/c_${portletId}/_addtolayout/${params.layoutId}`
})
return this.http
.put<
DotCMSResponse<string>
>(`${addToMenuUrl}/custom/c_${portletId}/_addtolayout/${params.layoutId}`, {})
.pipe(
pluck('entity'),
map((response) => response.entity),
catchError((error: HttpErrorResponse) => {
return this.httpErrorManagerService.handle(error).pipe(
take(1),
Expand Down
Loading
Loading