[ACS-11612] helium ACA e2es updates (#5159)

* [ACS-11612] helium ACA e2es updates

* [ACS-11612] helium testing week small tests fixes

* [ACS-11612] copilot review 1

* [ACS-11612] sonar fix 2

* [ACS-11612] copilot review fix 2

* [ACS-11612] shared links api small fixes

* [ACS-11612] sonar cloud fix 3

* [ACS-11612] fixed returns for promise<void> methods

* [ACS-11612] eslint fix and added override-ci skip in yml
This commit is contained in:
Adam Świderski
2026-04-22 11:26:39 +02:00
committed by GitHub
parent 7f05fc510c
commit a2631db0e2
15 changed files with 365 additions and 352 deletions
+5
View File
@@ -109,6 +109,7 @@ jobs:
run: npm run ci:test -- $TEST_OPTS run: npm run ci:test -- $TEST_OPTS
e2es-playwright: e2es-playwright:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'override-ci') }}
needs: [lint, build, unit-tests] needs: [lint, build, unit-tests]
name: E2E | ${{ matrix.browser || 'chromium' }} | ${{ matrix.e2e-suites.name }} | Playwright name: E2E | ${{ matrix.browser || 'chromium' }} | ${{ matrix.e2e-suites.name }} | Playwright
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -256,6 +257,10 @@ jobs:
}} }}
run: exit 1 run: exit 1
- name: Check e2e skipped
if: ${{ contains(github.event.pull_request.labels.*.name, 'override-ci') }}
run: echo "E2E tests were skipped due to override-ci label"
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
with: with:
@@ -1,100 +0,0 @@
/*!
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { test } from '@alfresco/aca-playwright-shared';
import { expect } from '@playwright/test';
export function favoritesTests(username: string) {
test.describe('Pagination controls : ', () => {
test.beforeEach(async ({ loginPage, favoritePage }) => {
await loginPage.navigate();
await loginPage.loginUser({ username: username, password: username });
await favoritePage.navigate();
await favoritePage.waitForPageLoad();
});
test('[XAT-4575] Pagination control default items', async ({ favoritePage }) => {
expect(await favoritePage.pagination.getRange()).toContain('1-25 of 51');
expect(await favoritePage.pagination.getMaxItems()).toContain('25');
expect(await favoritePage.pagination.getCurrentPage()).toContain('Page 1');
expect(await favoritePage.pagination.getTotalPages()).toContain('of 3');
expect(await favoritePage.pagination.isPreviousEnabled()).toBe(false);
expect(await favoritePage.pagination.isNextEnabled()).toBe(true);
});
test('[XAT-4576] Items per page values', async ({ favoritePage }) => {
await favoritePage.pagination.openMaxItemsMenu();
expect(await favoritePage.pagination.getItemsCount()).toBe(3);
await favoritePage.pagination.clickMenuItem('25');
await favoritePage.dataTable.spinnerWaitForReload();
expect(await favoritePage.pagination.getMaxItems()).toContain('25');
expect(await favoritePage.pagination.getTotalPages()).toContain('of 3');
await favoritePage.pagination.openMaxItemsMenu();
await favoritePage.pagination.clickMenuItem('50');
await favoritePage.dataTable.spinnerWaitForReload();
expect(await favoritePage.pagination.getMaxItems()).toContain('50');
expect(await favoritePage.pagination.getTotalPages()).toContain('of 2');
await favoritePage.pagination.closeMenu();
await favoritePage.pagination.openMaxItemsMenu();
await favoritePage.pagination.clickMenuItem('100');
await favoritePage.dataTable.spinnerWaitForReload();
expect(await favoritePage.pagination.getMaxItems()).toContain('100');
expect(await favoritePage.pagination.getTotalPages()).toContain('of 1');
await favoritePage.pagination.resetToDefaultPageSize();
});
test('[XAT-4578] Change the current page from the page selector', async ({ favoritePage }) => {
await favoritePage.pagination.clickOnNextPage();
expect(await favoritePage.pagination.getRange()).toContain('Showing 26-50 of 51');
expect(await favoritePage.pagination.getCurrentPage()).toContain('Page 2');
expect(await favoritePage.pagination.isPreviousEnabled()).toBe(true);
expect(await favoritePage.pagination.isNextEnabled()).toBe(true);
await favoritePage.pagination.resetToDefaultPageSize();
});
test('[XAT-4580] Next and Previous buttons navigation', async ({ favoritePage }) => {
await favoritePage.pagination.openMaxItemsMenu();
await favoritePage.pagination.clickMenuItem('25');
expect(await favoritePage.pagination.getMaxItems()).toContain('25');
await favoritePage.pagination.clickOnNextPage();
await favoritePage.dataTable.spinnerWaitForReload();
expect(await favoritePage.pagination.getRange()).toContain('Showing 26-50 of 51');
await favoritePage.pagination.clickOnPreviousPage();
await favoritePage.dataTable.spinnerWaitForReload();
expect(await favoritePage.pagination.getRange()).toContain('Showing 1-25 of 51');
});
test('[XAT-4579] Next button is disabled on last page', async ({ favoritePage }) => {
await favoritePage.pagination.openMaxItemsMenu();
await favoritePage.pagination.clickNthItem(3);
expect(await favoritePage.pagination.getCurrentPage()).toContain('Page 1');
expect(await favoritePage.pagination.isNextEnabled()).toBe(false);
});
});
}
@@ -22,9 +22,79 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>. * from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { ApiClientFactory, FavoritesPageApi, NodesApi, test, timeouts, Utils, TrashcanApi } from '@alfresco/aca-playwright-shared'; import {
import { personalFilesTests } from './personal-files'; ApiClientFactory,
import { favoritesTests } from './favorites'; DataTableComponent,
FavoritesPageApi,
NodesApi,
PaginationComponent,
SearchApi,
test,
timeouts,
Utils,
TrashcanApi
} from '@alfresco/aca-playwright-shared';
import { expect } from '@playwright/test';
async function assertDefaultPaginationState(pagination: PaginationComponent, range: string): Promise<void> {
expect(await pagination.getRange()).toContain(range);
expect(await pagination.getMaxItems()).toContain('25');
expect(await pagination.getCurrentPage()).toContain('Page 1');
expect(await pagination.getTotalPages()).toContain('of 3');
expect(await pagination.isPreviousEnabled()).toBe(false);
expect(await pagination.isNextEnabled()).toBe(true);
}
async function assertItemsPerPage(pagination: PaginationComponent, dataTable: DataTableComponent): Promise<void> {
await pagination.openMaxItemsMenu();
expect(await pagination.getItemsCount()).toBe(3);
await pagination.clickMenuItem('25');
await dataTable.spinnerWaitForReload();
expect(await pagination.getMaxItems()).toContain('25');
expect(await pagination.getTotalPages()).toContain('of 3');
await pagination.openMaxItemsMenu();
await pagination.clickMenuItem('50');
await dataTable.spinnerWaitForReload();
expect(await pagination.getMaxItems()).toContain('50');
expect(await pagination.getTotalPages()).toContain('of 2');
await pagination.openMaxItemsMenu();
await pagination.clickMenuItem('100');
await dataTable.spinnerWaitForReload();
expect(await pagination.getMaxItems()).toContain('100');
expect(await pagination.getTotalPages()).toContain('of 1');
await pagination.resetToDefaultPageSize();
}
async function assertNextPageNavigation(pagination: PaginationComponent): Promise<void> {
await pagination.clickOnNextPage();
expect(await pagination.getRange()).toContain('Showing 26-50 of 51');
expect(await pagination.getCurrentPage()).toContain('Page 2');
expect(await pagination.isPreviousEnabled()).toBe(true);
expect(await pagination.isNextEnabled()).toBe(true);
await pagination.resetToDefaultPageSize();
}
async function assertNextPreviousNavigation(pagination: PaginationComponent, dataTable: DataTableComponent): Promise<void> {
await pagination.openMaxItemsMenu();
await pagination.clickMenuItem('25');
expect(await pagination.getMaxItems()).toContain('25');
await pagination.clickOnNextPage();
await dataTable.spinnerWaitForReload();
expect(await pagination.getRange()).toContain('Showing 26-50 of 51');
await pagination.clickOnPreviousPage();
await dataTable.spinnerWaitForReload();
expect(await pagination.getRange()).toContain('Showing 1-25 of 51');
}
async function assertNextButtonDisabledOnLastPage(pagination: PaginationComponent): Promise<void> {
await pagination.openMaxItemsMenu();
await pagination.clickNthItem(3);
expect(await pagination.getCurrentPage()).toContain('Page 1');
expect(await pagination.isNextEnabled()).toBe(false);
}
test.describe('Pagination on multiple pages : ', () => { test.describe('Pagination on multiple pages : ', () => {
const random = Utils.random(); const random = Utils.random();
@@ -32,9 +102,11 @@ test.describe('Pagination on multiple pages : ', () => {
let nodesApi: NodesApi; let nodesApi: NodesApi;
let trashcanApi: TrashcanApi; let trashcanApi: TrashcanApi;
let favoritesApi: FavoritesPageApi; let favoritesApi: FavoritesPageApi;
let searchApi: SearchApi;
const parent = `parent-multi-${random}`; const parent = `parent-multi-${random}`;
let initialFavoritesTotalItems: number; let parentId: string;
let fileIds: string[];
const apiClientFactory = new ApiClientFactory(); const apiClientFactory = new ApiClientFactory();
@@ -43,17 +115,18 @@ test.describe('Pagination on multiple pages : ', () => {
await apiClientFactory.setUpAcaBackend('admin'); await apiClientFactory.setUpAcaBackend('admin');
await apiClientFactory.createUser({ username }); await apiClientFactory.createUser({ username });
nodesApi = await NodesApi.initialize(username, username); nodesApi = await NodesApi.initialize(username, username);
trashcanApi = await TrashcanApi.initialize(username, username);
favoritesApi = await FavoritesPageApi.initialize(username, username); favoritesApi = await FavoritesPageApi.initialize(username, username);
searchApi = await SearchApi.initialize(username, username);
const files = Array(51) const files = Array(51)
.fill('my-file') .fill('my-file')
.map((name, index): string => `${name}-${index + 1}-${random}.txt`); .map((name, index): string => `${name}-${index + 1}-${random}.txt`);
await nodesApi.createFolder(parent); parentId = (await nodesApi.createFolder(parent)).entry.id;
const filesIds = (await nodesApi.createFiles(files, parent)).list.entries.map((entries) => entries.entry.id); fileIds = (await nodesApi.createFiles(files, parent)).list?.entries?.map((entries) => entries.entry.id) ?? [];
initialFavoritesTotalItems = await favoritesApi.getFavoritesTotalItems(username);
await favoritesApi.addFavoritesByIds('file', filesIds); expect(fileIds).toHaveLength(51);
}); });
test.afterAll(async () => { test.afterAll(async () => {
@@ -61,14 +134,80 @@ test.describe('Pagination on multiple pages : ', () => {
}); });
test.describe('on Personal Files', () => { test.describe('on Personal Files', () => {
personalFilesTests(username, parent); test.beforeAll(async () => {
await searchApi.waitForFolderPathIndexing(parentId, { nodesExpected: 51 });
});
test.describe('Pagination controls : ', () => {
test.beforeEach(async ({ loginPage, personalFiles, page }) => {
await loginPage.navigate();
await loginPage.loginUser({ username: username, password: username });
await personalFiles.waitForPageLoad();
await personalFiles.dataTable.getRowByName(parent).dblclick();
await page.waitForTimeout(timeouts.tiny);
});
test('[XAT-4530] Pagination control default items', async ({ personalFiles }) => {
await assertDefaultPaginationState(personalFiles.pagination, 'Showing 1-25 of 51');
});
test('[XAT-4531] Items per page values', async ({ personalFiles }) => {
await assertItemsPerPage(personalFiles.pagination, personalFiles.dataTable);
});
test('[XAT-4533] Change the current page from the page selector', async ({ personalFiles }) => {
await assertNextPageNavigation(personalFiles.pagination);
});
test('[XAT-4536] Next and Previous buttons navigation', async ({ personalFiles }) => {
await assertNextPreviousNavigation(personalFiles.pagination, personalFiles.dataTable);
});
test('[XAT-4534] Previous button is disabled on first page', async ({ personalFiles }) => {
expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
expect(await personalFiles.pagination.isPreviousEnabled()).toBe(false);
});
test('[XAT-4535] Next button is disabled on last page', async ({ personalFiles }) => {
await assertNextButtonDisabledOnLastPage(personalFiles.pagination);
});
});
}); });
test.describe('on Favorites', () => { test.describe('on Favorites', () => {
test.beforeAll(async () => { test.beforeAll(async () => {
const initialFavoritesTotalItems = await favoritesApi.getFavoritesTotalItems(username);
await favoritesApi.addFavoritesByIds('file', fileIds);
await favoritesApi.waitForApi(username, { expect: initialFavoritesTotalItems + 51 }); await favoritesApi.waitForApi(username, { expect: initialFavoritesTotalItems + 51 });
}); });
favoritesTests(username); test.describe('Pagination controls : ', () => {
test.beforeEach(async ({ loginPage, favoritePage }) => {
await loginPage.navigate();
await loginPage.loginUser({ username: username, password: username });
await favoritePage.navigate();
await favoritePage.waitForPageLoad();
});
test('[XAT-4575] Pagination control default items', async ({ favoritePage }) => {
await assertDefaultPaginationState(favoritePage.pagination, '1-25 of 51');
});
test('[XAT-4576] Items per page values', async ({ favoritePage }) => {
await assertItemsPerPage(favoritePage.pagination, favoritePage.dataTable);
});
test('[XAT-4578] Change the current page from the page selector', async ({ favoritePage }) => {
await assertNextPageNavigation(favoritePage.pagination);
});
test('[XAT-4580] Next and Previous buttons navigation', async ({ favoritePage }) => {
await assertNextPreviousNavigation(favoritePage.pagination, favoritePage.dataTable);
});
test('[XAT-4579] Next button is disabled on last page', async ({ favoritePage }) => {
await assertNextButtonDisabledOnLastPage(favoritePage.pagination);
});
});
}); });
}); });
@@ -1,101 +0,0 @@
/*!
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { test, timeouts } from '@alfresco/aca-playwright-shared';
import { expect } from '@playwright/test';
export function personalFilesTests(userName: string, parentName: string) {
test.describe('Pagination controls : ', () => {
test.beforeEach(async ({ loginPage, personalFiles, page }) => {
await loginPage.navigate();
await loginPage.loginUser({ username: userName, password: userName });
await personalFiles.waitForPageLoad();
await personalFiles.dataTable.getRowByName(parentName).dblclick();
await page.waitForTimeout(timeouts.tiny);
});
test('[XAT-4530] Pagination control default items', async ({ personalFiles }) => {
expect(await personalFiles.pagination.getRange()).toContain('Showing 1-25 of 51');
expect(await personalFiles.pagination.getMaxItems()).toContain('25');
expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
expect(await personalFiles.pagination.getTotalPages()).toContain('of 3');
expect(await personalFiles.pagination.isPreviousEnabled()).toBe(false);
expect(await personalFiles.pagination.isNextEnabled()).toBe(true);
});
test('[XAT-4531] Items per page values', async ({ personalFiles }) => {
await personalFiles.pagination.openMaxItemsMenu();
expect(await personalFiles.pagination.getItemsCount()).toBe(3);
await personalFiles.pagination.clickMenuItem('25');
await personalFiles.dataTable.spinnerWaitForReload();
expect(await personalFiles.pagination.getMaxItems()).toContain('25');
expect(await personalFiles.pagination.getTotalPages()).toContain('of 3');
await personalFiles.pagination.openMaxItemsMenu();
await personalFiles.pagination.clickMenuItem('50');
expect(await personalFiles.pagination.getMaxItems()).toContain('50');
expect(await personalFiles.pagination.getTotalPages()).toContain('of 2');
await personalFiles.pagination.openMaxItemsMenu();
await personalFiles.pagination.clickMenuItem('100');
expect(await personalFiles.pagination.getMaxItems()).toContain('100');
expect(await personalFiles.pagination.getTotalPages()).toContain('of 1');
await personalFiles.pagination.resetToDefaultPageSize();
});
test('[XAT-4533] Change the current page from the page selector', async ({ personalFiles }) => {
await personalFiles.pagination.clickOnNextPage();
expect(await personalFiles.pagination.getRange()).toContain('Showing 26-50 of 51');
expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 2');
expect(await personalFiles.pagination.isPreviousEnabled()).toBe(true);
expect(await personalFiles.pagination.isNextEnabled()).toBe(true);
await personalFiles.pagination.resetToDefaultPageSize();
});
test('[XAT-4536] Next and Previous buttons navigation', async ({ personalFiles }) => {
await personalFiles.pagination.openMaxItemsMenu();
await personalFiles.pagination.clickMenuItem('25');
expect(await personalFiles.pagination.getMaxItems()).toContain('25');
await personalFiles.pagination.clickOnNextPage();
await personalFiles.dataTable.spinnerWaitForReload();
expect(await personalFiles.pagination.getRange()).toContain('Showing 26-50 of 51');
await personalFiles.pagination.clickOnPreviousPage();
await personalFiles.dataTable.spinnerWaitForReload();
expect(await personalFiles.pagination.getRange()).toContain('Showing 1-25 of 51');
});
test('[XAT-4534] Previous button is disabled on first page', async ({ personalFiles }) => {
expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
expect(await personalFiles.pagination.isPreviousEnabled()).toBe(false);
});
test('[XAT-4535] Next button is disabled on last page', async ({ personalFiles }) => {
await personalFiles.pagination.openMaxItemsMenu();
await personalFiles.pagination.clickNthItem(3);
expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
expect(await personalFiles.pagination.isNextEnabled()).toBe(false);
});
});
}
@@ -45,6 +45,7 @@ import {
TagsApi TagsApi
} from '@alfresco/js-api'; } from '@alfresco/js-api';
import { users } from '../base-config'; import { users } from '../base-config';
import { logger } from '../utils';
import { Person, PersonModel } from './people-api-models'; import { Person, PersonModel } from './people-api-models';
export interface AcaBackend { export interface AcaBackend {
@@ -89,13 +90,9 @@ export class ApiClientFactory {
public queriesApi: QueriesApi; public queriesApi: QueriesApi;
public categoriesApi: CategoriesApi; public categoriesApi: CategoriesApi;
public tagsApi: TagsApi; public tagsApi: TagsApi;
constructor() { constructor() {
this.alfrescoApi = new AlfrescoApi(config); this.alfrescoApi = new AlfrescoApi(config);
}
public async setUpAcaBackend(userName: string, password?: string): Promise<AcaBackend> {
await this.login(userName, password);
this.sites = new SitesApi(this.alfrescoApi); this.sites = new SitesApi(this.alfrescoApi);
this.upload = new UploadApi(this.alfrescoApi); this.upload = new UploadApi(this.alfrescoApi);
this.nodes = new NodesApi(this.alfrescoApi); this.nodes = new NodesApi(this.alfrescoApi);
@@ -107,6 +104,7 @@ export class ApiClientFactory {
this.search = new SearchApi(this.alfrescoApi); this.search = new SearchApi(this.alfrescoApi);
this.securityGroupsApi = new SecurityGroupsApi(this.alfrescoApi); this.securityGroupsApi = new SecurityGroupsApi(this.alfrescoApi);
this.securityMarksApi = new SecurityMarksApi(this.alfrescoApi); this.securityMarksApi = new SecurityMarksApi(this.alfrescoApi);
this.contentClient = new ContentClient(config, '/alfresco');
this.share = new SharedlinksApi(this.alfrescoApi); this.share = new SharedlinksApi(this.alfrescoApi);
this.favorites = new FavoritesApi(this.alfrescoApi); this.favorites = new FavoritesApi(this.alfrescoApi);
this.trashCan = new TrashcanApi(this.alfrescoApi); this.trashCan = new TrashcanApi(this.alfrescoApi);
@@ -114,7 +112,10 @@ export class ApiClientFactory {
this.queriesApi = new QueriesApi(this.alfrescoApi); this.queriesApi = new QueriesApi(this.alfrescoApi);
this.categoriesApi = new CategoriesApi(this.alfrescoApi); this.categoriesApi = new CategoriesApi(this.alfrescoApi);
this.tagsApi = new TagsApi(this.alfrescoApi); this.tagsApi = new TagsApi(this.alfrescoApi);
}
public async setUpAcaBackend(userName: string, password?: string): Promise<AcaBackend> {
await this.login(userName, password);
return this; return this;
} }
@@ -124,49 +125,55 @@ export class ApiClientFactory {
} }
async login(userName: string, password?: string) { async login(userName: string, password?: string) {
const predefinedUserKey = Object.keys(users).find((user) => user === userName || users[user].username === userName); const predefinedUserKey = Object.keys(users).find(
(userAlias) => userAlias === userName || users[userAlias as keyof typeof users].username === userName
) as keyof typeof users | undefined;
const userToLog = predefinedUserKey ? users[predefinedUserKey] : undefined; const userToLog = predefinedUserKey ? users[predefinedUserKey] : undefined;
let e: any;
const user = userToLog?.username ?? userName; const user = userToLog?.username ?? userName;
const userPassword = userToLog?.password ?? password; const userPassword = userToLog?.password ?? password;
if (!userPassword) {
throw new Error(`[API Client Factory] No password provided for user ${user}`);
}
try { try {
e = await this.alfrescoApi.login(user, userPassword); await this.alfrescoApi.login(user, userPassword);
} catch (error) { } catch (error) {
console.error(`[API Client Factory] Log in user ${user} failed ${e}`); logger.error(`[API Client Factory] Log in user ${user} failed ${error}`);
throw error; throw error;
} }
} }
async loginUser(user: PersonModel) { async loginUser(user: PersonModel) {
let e: any; if (!user.username || !user.password) {
throw new Error(`[API Client Factory] No username or password provided`);
}
try { try {
e = await this.alfrescoApi.login(user.username, user.password); await this.alfrescoApi.login(user.username, user.password);
} catch (error) { } catch (error) {
console.error(`[API Client Factory] Log in user ${user.username} failed ${e}`); logger.error(`[API Client Factory] Log in user ${user.username} failed ${error}`);
throw error; throw error;
} }
} }
async createUser(user: PersonModel): Promise<PersonEntry> { async createUser(user: PersonModel): Promise<PersonEntry | null> {
const person = new Person(user); const person = new Person(user);
const peopleApi = new PeopleApi(this.alfrescoApi); const peopleApi = new PeopleApi(this.alfrescoApi);
try { try {
return peopleApi.createPerson(person); return await peopleApi.createPerson(person);
} catch (error) { } catch (error) {
console.error('[API Client Factory] createUser failed : ', error); logger.error(`[API Client Factory] createUser failed: ${error}`);
return null; return null;
} }
} }
async changePassword(username: string, newPassword: string): Promise<PersonEntry> { async changePassword(username: string, newPassword: string): Promise<PersonEntry | null> {
const peopleApi = new PeopleApi(this.alfrescoApi); const peopleApi = new PeopleApi(this.alfrescoApi);
try { try {
return peopleApi.updatePerson(username, { password: newPassword }); return await peopleApi.updatePerson(username, { password: newPassword });
} catch (error) { } catch (error) {
console.error('[API Client Factory] changePassword failed : ', error); logger.error(`[API Client Factory] changePassword failed: ${error}`);
return null; return null;
} }
} }
@@ -23,6 +23,7 @@
*/ */
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { logger } from '../utils';
import { CategoryEntry, CategoryBody, CategoryQuery, CategoryPaging, CategoryLinkBody } from '@alfresco/js-api'; import { CategoryEntry, CategoryBody, CategoryQuery, CategoryPaging, CategoryLinkBody } from '@alfresco/js-api';
export class CategoriesApi { export class CategoriesApi {
@@ -42,21 +43,21 @@ export class CategoriesApi {
try { try {
return this.apiService.categoriesApi.createSubcategories(categoryId, categoryBodyCreate, opts); return this.apiService.categoriesApi.createSubcategories(categoryId, categoryBodyCreate, opts);
} catch (error) { } catch (error) {
console.error(error); logger.error(`${error}`);
return null; return null;
} }
} }
async deleteCategory(categoryId: string): Promise<void> { async deleteCategory(categoryId?: string): Promise<void> {
if (categoryId === null) { if (!categoryId) {
console.error('categoryId is null, skipping deletion'); logger.error('categoryId is null, skipping deletion');
return; return;
} }
try { try {
await this.apiService.categoriesApi.deleteCategory(categoryId); await this.apiService.categoriesApi.deleteCategory(categoryId);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteCategory.name}: ${error}`); logger.error(`${this.constructor.name} ${this.deleteCategory.name}: ${error}`);
} }
} }
@@ -68,7 +69,7 @@ export class CategoriesApi {
try { try {
return this.apiService.categoriesApi.linkNodeToCategory(nodeId, categoryLinkBodyCreate, opts); return this.apiService.categoriesApi.linkNodeToCategory(nodeId, categoryLinkBodyCreate, opts);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.linkNodeToCategory.name}: ${error}`); logger.error(`${this.constructor.name} ${this.linkNodeToCategory.name}: ${error}`);
return null; return null;
} }
} }
@@ -24,7 +24,7 @@
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { FavoriteEntry, FavoritePaging } from '@alfresco/js-api'; import { FavoriteEntry, FavoritePaging } from '@alfresco/js-api';
import { Utils } from '../utils'; import { logger, Utils } from '../utils';
export class FavoritesPageApi { export class FavoritesPageApi {
private readonly apiService: ApiClientFactory; private readonly apiService: ApiClientFactory;
@@ -124,7 +124,7 @@ export class FavoritesPageApi {
} }
} }
} catch (error) { } catch (error) {
console.error('FavoritesApi: removeFavoritesByIds failed ', error); logger.error(`FavoritesApi: removeFavoritesByIds failed: ${error}`);
} }
} }
} }
@@ -24,7 +24,7 @@
import * as fs from 'fs'; import * as fs from 'fs';
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { Utils, waitForApi } from '../utils'; import { logger, Utils, waitForApi } from '../utils';
import { NodeBodyCreate, NodeEntry, ResultSetPaging, SearchRequest } from '@alfresco/js-api'; import { NodeBodyCreate, NodeEntry, ResultSetPaging, SearchRequest } from '@alfresco/js-api';
export class FileActionsApi { export class FileActionsApi {
@@ -161,7 +161,9 @@ export class FileActionsApi {
await waitForApi(apiCall, predicate, 30, 2500); await waitForApi(apiCall, predicate, 30, 2500);
} catch { } catch {
const actual = await apiCall(); const actual = await apiCall();
throw new Error(`waitForNodes: Timed out waiting for "${searchTerm}" — expected ${data.expect} nodes, found ${actual}`); const message = `waitForNodes: Timed out waiting for "${searchTerm}" — expected ${data.expect} nodes, found ${actual}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -208,16 +210,16 @@ export class FileActionsApi {
try { try {
return (await this.queryNodesSearchHighlight(searchTerm)).list.pagination.totalItems; return (await this.queryNodesSearchHighlight(searchTerm)).list.pagination.totalItems;
} catch (error) { } catch (error) {
console.warn(`queryNodesSearchHighlight failed for "${searchTerm}":`, error); logger.warn(`queryNodesSearchHighlight failed for "${searchTerm}": ${error}`);
return 0; return 0;
} }
}; };
try { try {
await waitForApi(apiCall, predicate, 30, 2500); await waitForApi(apiCall, predicate, 30, 2500);
console.log(`waitForNodesSearchHighlight: Found ${data.expect} nodes with search term "${searchTerm}"`); logger.log(`waitForNodesSearchHighlight: Found ${data.expect} nodes with search term "${searchTerm}"`);
} catch (error) { } catch (error) {
console.error(`Error: ${error}`); logger.error(`Error: ${error}`);
} }
} }
@@ -228,9 +230,9 @@ export class FileActionsApi {
comment: comment, comment: comment,
name: newName name: newName
}; };
return this.apiService.nodes.updateNodeContent(nodeId, content, opts); return await this.apiService.nodes.updateNodeContent(nodeId, content, opts);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.updateNodeContent.name}`, error); logger.error(`${this.constructor.name} ${this.updateNodeContent.name}: ${error}`);
return Promise.reject(error); return Promise.reject(error);
} }
} }
@@ -25,7 +25,7 @@
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { NodeChildAssociationPaging, NodeEntry, NodePaging, NodesIncludeQuery, NodeBodyUpdate } from '@alfresco/js-api'; import { NodeChildAssociationPaging, NodeEntry, NodePaging, NodesIncludeQuery, NodeBodyUpdate } from '@alfresco/js-api';
import { NodeContentTree, flattenNodeContentTree } from './node-content-tree'; import { NodeContentTree, flattenNodeContentTree } from './node-content-tree';
import { Utils } from '../utils'; import { logger, Utils } from '../utils';
export class NodesApi { export class NodesApi {
private readonly apiService: ApiClientFactory; private readonly apiService: ApiClientFactory;
@@ -51,7 +51,9 @@ export class NodesApi {
try { try {
return await this.createNode('cm:folder', name, parentId, title, description, null, author, true, aspectNames); return await this.createNode('cm:folder', name, parentId, title, description, null, author, true, aspectNames);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createFolder.name}: ${error}`); const message = `${this.constructor.name} ${this.createFolder.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -67,7 +69,9 @@ export class NodesApi {
try { try {
return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames); return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createFile.name}: ${error}`); const message = `${this.constructor.name} ${this.createFile.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -75,7 +79,9 @@ export class NodesApi {
try { try {
return await this.createContent({ files: names }, relativePath); return await this.createContent({ files: names }, relativePath);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createFiles.name}: ${error}`); const message = `${this.constructor.name} ${this.createFiles.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -87,6 +93,9 @@ export class NodesApi {
createdFiles.push(file); createdFiles.push(file);
} }
} }
logger.info(
`${this.constructor.name} ${this.createMultipleFiles.name}: created ${createdFiles.length} of ${count} files in parent "${parentId}"`
);
return createdFiles; return createdFiles;
} }
@@ -94,7 +103,9 @@ export class NodesApi {
try { try {
return await this.createContent({ folders: names }, relativePath); return await this.createContent({ folders: names }, relativePath);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createFolders.name}: ${error}`); const message = `${this.constructor.name} ${this.createFolders.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -102,7 +113,7 @@ export class NodesApi {
try { try {
await this.apiService.trashCan.deleteDeletedNode(name); await this.apiService.trashCan.deleteDeletedNode(name);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteDeletedNode.name}: ${error}`); logger.error(`${this.constructor.name} ${this.deleteDeletedNode.name}: ${error}`);
} }
} }
@@ -133,11 +144,13 @@ export class NodesApi {
} }
try { try {
return this.apiService.nodes.createNode(parentId, nodeBody, { return await this.apiService.nodes.createNode(parentId, nodeBody, {
majorVersion majorVersion
}); });
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createNode.name}: ${error}`); const message = `${this.constructor.name} ${this.createNode.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -145,7 +158,7 @@ export class NodesApi {
try { try {
return this.apiService.nodes.updateNode(nodeId, { name: newName }); return this.apiService.nodes.updateNode(nodeId, { name: newName });
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.renameNode.name}`, error); logger.error(`${this.constructor.name} ${this.renameNode.name}: ${error}`);
return null; return null;
} }
} }
@@ -159,7 +172,7 @@ export class NodesApi {
try { try {
await this.apiService.nodes.deleteNodes(nodeIds, { permanent }); await this.apiService.nodes.deleteNodes(nodeIds, { permanent });
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteNodes.name}`, error); logger.error(`${this.constructor.name} ${this.deleteNodes.name}: ${error}`);
} }
} }
@@ -167,7 +180,7 @@ export class NodesApi {
try { try {
return await this.apiService.nodes.updateNode(nodeId, nodeBodyUpdate, opts); return await this.apiService.nodes.updateNode(nodeId, nodeBodyUpdate, opts);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.updateNode.name}`, error); logger.error(`${this.constructor.name} ${this.updateNode.name}: ${error}`);
return null; return null;
} }
} }
@@ -182,7 +195,7 @@ export class NodesApi {
const userNodesIds = userNodes.map((nodeChild) => nodeChild.entry.id); const userNodesIds = userNodes.map((nodeChild) => nodeChild.entry.id);
await this.deleteNodes(userNodesIds); await this.deleteNodes(userNodesIds);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteCurrentUserNodes.name}`, error); logger.error(`${this.constructor.name} ${this.deleteCurrentUserNodes.name}: ${error}`);
} }
} }
@@ -192,7 +205,7 @@ export class NodesApi {
await this.apiService.nodes.lockNode(nodeId, { type: lockType }); await this.apiService.nodes.lockNode(nodeId, { type: lockType });
} }
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.lockNodes.name}`, error); logger.error(`${this.constructor.name} ${this.lockNodes.name}: ${error}`);
} }
} }
@@ -202,7 +215,7 @@ export class NodesApi {
await this.apiService.nodes.unlockNode(nodeId); await this.apiService.nodes.unlockNode(nodeId);
} }
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.unlockNodes.name}`, error); logger.error(`${this.constructor.name} ${this.unlockNodes.name}: ${error}`);
} }
} }
@@ -210,7 +223,9 @@ export class NodesApi {
try { try {
return this.apiService.nodes.createNode('-my-', flattenNodeContentTree(content, relativePath) as any); return this.apiService.nodes.createNode('-my-', flattenNodeContentTree(content, relativePath) as any);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.createContent.name}: ${error}`); const message = `${this.constructor.name} ${this.createContent.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -218,7 +233,9 @@ export class NodesApi {
try { try {
return this.apiService.nodes.getNode(id); return this.apiService.nodes.getNode(id);
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.getNodeById.name}: ${error}`); const message = `${this.constructor.name} ${this.getNodeById.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -227,7 +244,7 @@ export class NodesApi {
const children = (await this.getNodeChildren(parentId))?.list?.entries ?? []; const children = (await this.getNodeChildren(parentId))?.list?.entries ?? [];
return children.find((elem) => elem.entry.name === name)?.entry.id ?? ''; return children.find((elem) => elem.entry.name === name)?.entry.id ?? '';
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.getNodeIdFromParent.name}`, error); logger.error(`${this.constructor.name} ${this.getNodeIdFromParent.name}: ${error}`);
return ''; return '';
} }
} }
@@ -239,7 +256,7 @@ export class NodesApi {
}; };
return this.apiService.nodes.listNodeChildren(nodeId, opts); return this.apiService.nodes.listNodeChildren(nodeId, opts);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.getNodeChildren.name}`, error); logger.error(`${this.constructor.name} ${this.getNodeChildren.name}: ${error}`);
return null; return null;
} }
} }
@@ -248,7 +265,7 @@ export class NodesApi {
try { try {
await this.apiService.nodes.deleteNode(id, { permanent }); await this.apiService.nodes.deleteNode(id, { permanent });
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteNodeById.name}`, error); logger.error(`${this.constructor.name} ${this.deleteNodeById.name}: ${error}`);
} }
} }
@@ -260,7 +277,7 @@ export class NodesApi {
await this.deleteNodeById(nodeId); await this.deleteNodeById(nodeId);
} }
} catch (error) { } catch (error) {
console.error('Admin Actions - cleanupNodeTemplatesItems failed : ', error); logger.error(`Admin Actions - cleanupNodeTemplatesItems failed: ${error}`);
} }
} }
@@ -272,7 +289,7 @@ export class NodesApi {
await this.deleteNodeById(nodeId); await this.deleteNodeById(nodeId);
} }
} catch (error) { } catch (error) {
console.error('Admin Actions - cleanupSpaceTemplatesFolder failed : ', error); logger.error(`Admin Actions - cleanupSpaceTemplatesFolder failed: ${error}`);
} }
} }
@@ -280,7 +297,7 @@ export class NodesApi {
try { try {
return this.getNodeIdFromParent('Node Templates', await this.getDataDictionaryId()); return this.getNodeIdFromParent('Node Templates', await this.getDataDictionaryId());
} catch (error) { } catch (error) {
console.error('Admin Actions - getNodeTemplatesFolderId failed : ', error); logger.error(`Admin Actions - getNodeTemplatesFolderId failed: ${error}`);
return ''; return '';
} }
} }
@@ -289,14 +306,14 @@ export class NodesApi {
try { try {
return this.getNodeIdFromParent('Space Templates', await this.getDataDictionaryId()); return this.getNodeIdFromParent('Space Templates', await this.getDataDictionaryId());
} catch (error) { } catch (error) {
console.error('Admin Actions - getSpaceTemplatesFolderId failed : ', error); logger.error(`Admin Actions - getSpaceTemplatesFolderId failed: ${error}`);
return ''; return '';
} }
} }
private async getDataDictionaryId(): Promise<string> { private async getDataDictionaryId(): Promise<string> {
return this.getNodeIdFromParent('Data Dictionary', '-root-').catch((error) => { return this.getNodeIdFromParent('Data Dictionary', '-root-').catch((error) => {
console.error('Admin Actions - getDataDictionaryId failed : ', error); logger.error(`Admin Actions - getDataDictionaryId failed: ${error}`);
return ''; return '';
}); });
} }
@@ -317,7 +334,7 @@ export class NodesApi {
try { try {
return this.apiService.nodes.updateNode(nodeId, data); return this.apiService.nodes.updateNode(nodeId, data);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.setGranularPermission.name}`, error); logger.error(`${this.constructor.name} ${this.setGranularPermission.name}: ${error}`);
return null; return null;
} }
} }
@@ -329,7 +346,7 @@ export class NodesApi {
return this.setInheritPermissions(nodeId, false); return this.setInheritPermissions(nodeId, false);
} catch (error) { } catch (error) {
console.error('Admin Actions - removeUserAccessOnNodeTemplate failed : ', error); logger.error(`Admin Actions - removeUserAccessOnNodeTemplate failed: ${error}`);
return null; return null;
} }
} }
@@ -341,7 +358,7 @@ export class NodesApi {
return this.setInheritPermissions(nodeId, false); return this.setInheritPermissions(nodeId, false);
} catch (error) { } catch (error) {
console.error('Admin Actions - removeUserAccessOnSpaceTemplate failed : ', error); logger.error(`Admin Actions - removeUserAccessOnSpaceTemplate failed: ${error}`);
return null; return null;
} }
} }
@@ -356,7 +373,7 @@ export class NodesApi {
try { try {
return this.apiService.nodes.updateNode(nodeId, data); return this.apiService.nodes.updateNode(nodeId, data);
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.setInheritPermissions.name}`, error); logger.error(`${this.constructor.name} ${this.setInheritPermissions.name}: ${error}`);
return null; return null;
} }
} }
@@ -365,7 +382,7 @@ export class NodesApi {
try { try {
return this.apiService.nodes.updateNode(nodeId, { aspectNames }); return this.apiService.nodes.updateNode(nodeId, { aspectNames });
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.addAspects.name}`, error); logger.error(`${this.constructor.name} ${this.addAspects.name}: ${error}`);
return null; return null;
} }
} }
@@ -385,7 +402,7 @@ export class NodesApi {
await this.addAspects(originalNodeId, ['app:linked']); await this.addAspects(originalNodeId, ['app:linked']);
return link; return link;
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.createFileLink.name}`, error); logger.error(`${this.constructor.name} ${this.createFileLink.name}: ${error}`);
return null; return null;
} }
} }
@@ -408,7 +425,7 @@ export class NodesApi {
await this.addAspects(originalNodeId, ['app:linked']); await this.addAspects(originalNodeId, ['app:linked']);
return link; return link;
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.createFolderLink.name}`, error); logger.error(`${this.constructor.name} ${this.createFolderLink.name}: ${error}`);
return null; return null;
} }
} }
@@ -421,7 +438,9 @@ export class NodesApi {
return this.createFileLink(nodeId, destinationParentId); return this.createFileLink(nodeId, destinationParentId);
} catch (error) { } catch (error) {
throw new Error(`Admin Actions - createLinkToFileName failed : ${error}`); const message = `Admin Actions - createLinkToFileName failed : ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -432,7 +451,9 @@ export class NodesApi {
const nodeId = await this.getNodeIdFromParent(originalFolderName, originalFolderParentId); const nodeId = await this.getNodeIdFromParent(originalFolderName, originalFolderParentId);
return this.createFolderLink(nodeId, destinationParentId); return this.createFolderLink(nodeId, destinationParentId);
} catch (error) { } catch (error) {
throw new Error(`Admin Actions - createLinkToFolderName failed : ${error}`); const message = `Admin Actions - createLinkToFolderName failed : ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -441,7 +462,9 @@ export class NodesApi {
const node = await this.getNodeById(nodeId); const node = await this.getNodeById(nodeId);
return node.entry.properties?.[property] ?? ''; return node.entry.properties?.[property] ?? '';
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.getNodeProperty.name}: ${error}`); const message = `${this.constructor.name} ${this.getNodeProperty.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -450,7 +473,9 @@ export class NodesApi {
const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId'); const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId');
return sharedId !== ''; return sharedId !== '';
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.isFileShared.name}: ${error}`); const message = `${this.constructor.name} ${this.isFileShared.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -459,7 +484,9 @@ export class NodesApi {
const lockType = await this.getNodeProperty(nodeId, 'cm:lockType'); const lockType = await this.getNodeProperty(nodeId, 'cm:lockType');
return lockType || ''; return lockType || '';
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.getLockType.name}: ${error}`); const message = `${this.constructor.name} ${this.getLockType.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -467,7 +494,9 @@ export class NodesApi {
try { try {
return (await this.getLockType(nodeId)) === 'WRITE_LOCK'; return (await this.getLockType(nodeId)) === 'WRITE_LOCK';
} catch (error) { } catch (error) {
throw new Error(`${this.constructor.name} ${this.isFileLockedWrite.name}: ${error}`); const message = `${this.constructor.name} ${this.isFileLockedWrite.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
} }
@@ -23,7 +23,7 @@
*/ */
import { FindQuery } from '@alfresco/js-api'; import { FindQuery } from '@alfresco/js-api';
import { Utils } from '../utils'; import { logger, Utils } from '../utils';
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
export class QueriesApi { export class QueriesApi {
@@ -52,8 +52,7 @@ export class QueriesApi {
return await Utils.retryCall(sites); return await Utils.retryCall(sites);
} catch (error) { } catch (error) {
console.error(`QueriesApi waitForSites : catch : `); logger.error(`QueriesApi waitForSites : catch : Expected: ${data.expect} items, but found ${error}`);
console.error(`\tExpected: ${data.expect} items, but found ${error}`);
return null; return null;
} }
} }
@@ -68,7 +67,7 @@ export class QueriesApi {
const sites = await this.apiService.queries.findSites(searchTerm, opts); const sites = await this.apiService.queries.findSites(searchTerm, opts);
return sites.list.pagination.totalItems; return sites.list.pagination.totalItems;
} catch (error) { } catch (error) {
console.error(`QueriesApi findSitesTotalItems : catch :`, error); logger.error(`QueriesApi findSitesTotalItems : catch : ${error}`);
return null; return null;
} }
} }
@@ -23,7 +23,7 @@
*/ */
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { Utils } from '../utils'; import { logger, Utils } from '../utils';
import { ResultSetPaging, SearchRequest } from '@alfresco/js-api'; import { ResultSetPaging, SearchRequest } from '@alfresco/js-api';
export class SearchApi { export class SearchApi {
@@ -59,7 +59,7 @@ export class SearchApi {
} }
async getTotalItems(username: string): Promise<number> { async getTotalItems(username: string): Promise<number> {
return (await this.querySearchFiles(username)).list.pagination.totalItems; return (await this.querySearchFiles(username)).list?.pagination?.totalItems ?? 0;
} }
async waitForApi(username: string, data: { expect: number }) { async waitForApi(username: string, data: { expect: number }) {
@@ -92,29 +92,31 @@ export class SearchApi {
do { do {
result = await this.apiService.search.search(query); result = await this.apiService.search.search(query);
const currentCount = result.list.pagination.count; const currentCount = result.list?.pagination?.count ?? 0;
if (currentCount !== options.nodesExpected) { if (currentCount !== options.nodesExpected) {
retryCount++; retryCount++;
if (retryCount % 30 === 0) { if (retryCount % 30 === 0) {
console.info( logger.info(
`waitForFolderPathIndexing: After ${retryCount} seconds, expected ${options.nodesExpected} nodes but found ${currentCount} in folder ${folderId}` `waitForFolderPathIndexing: After ${retryCount} seconds, expected ${options.nodesExpected} nodes but found ${currentCount} in folder ${folderId}`
); );
} }
if (retryCount >= retryLimit) { if (retryCount >= retryLimit) {
throw new Error(`Expected ${options.nodesExpected} nodes but found ${currentCount} after ${retryLimit} retries`); const message = `Expected ${options.nodesExpected} nodes but found ${currentCount} after ${retryLimit} retries`;
logger.error(message);
throw new Error(message);
} }
await Utils.delayInSeconds(1); await Utils.delayInSeconds(1);
} }
} while (result.list.pagination.count !== options.nodesExpected); } while (result.list?.pagination?.count !== options.nodesExpected);
console.info(`waitForFolderPathIndexing: Found expected ${options.nodesExpected} nodes in folder ${folderId}`); logger.info(`waitForFolderPathIndexing: Found expected ${options.nodesExpected} nodes in folder ${folderId}`);
return result.list.pagination.count; return result.list?.pagination?.count ?? 0;
} catch (error) { } catch (error) {
console.error(`waitForFolderPathIndexing failed for folderId "${folderId}": ${error}`); logger.error(`waitForFolderPathIndexing failed for folderId "${folderId}": ${error}`);
throw error; throw error;
} }
} }
@@ -24,7 +24,7 @@
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { SharedLinkEntry, SharedLinkPaging } from '@alfresco/js-api'; import { SharedLinkEntry, SharedLinkPaging } from '@alfresco/js-api';
import { Utils } from '../utils'; import { logger, Utils } from '../utils';
export class SharedLinksApi { export class SharedLinksApi {
private readonly apiService: ApiClientFactory; private readonly apiService: ApiClientFactory;
@@ -44,7 +44,7 @@ export class SharedLinksApi {
nodeId: id, nodeId: id,
expiresAt: expireDate expiresAt: expireDate
}; };
return this.apiService.share.createSharedLink(data); return await this.apiService.share.createSharedLink(data);
} catch (error) { } catch (error) {
return null; return null;
} }
@@ -56,11 +56,13 @@ export class SharedLinksApi {
if (ids && ids.length > 0) { if (ids && ids.length > 0) {
for (const id of ids) { for (const id of ids) {
const sharedLink = await this.shareFileById(id, expireDate); const sharedLink = await this.shareFileById(id, expireDate);
sharedLinks.push(sharedLink); if (sharedLink) {
sharedLinks.push(sharedLink);
}
} }
} }
} catch (error) { } catch (error) {
console.error(`SharedLinksApi shareFilesByIds : catch : `, error); logger.error(`SharedLinksApi shareFilesByIds : catch : ${error}`);
} }
return sharedLinks; return sharedLinks;
} }
@@ -70,72 +72,68 @@ export class SharedLinksApi {
const opts = { const opts = {
maxItems maxItems
}; };
return this.apiService.share.listSharedLinks(opts); return await this.apiService.share.listSharedLinks(opts);
} catch (error) { } catch (error) {
console.error(`SharedLinksApi getSharedLinks : catch : `, error); logger.error(`SharedLinksApi getSharedLinks : catch : ${error}`);
return new SharedLinkPaging(); return new SharedLinkPaging();
} }
} }
async waitForFilesToBeShared(filesIds: string[]): Promise<void> { async waitForFilesToBeShared(fileIds: string[]): Promise<void> {
try { try {
const sharedFile = async () => { const sharedFile = async () => {
const sharedFiles = (await this.getSharedLinks()).list.entries.map((link) => link.entry.nodeId); const sharedFiles = (await this.getSharedLinks()).list?.entries?.map((link) => link.entry.nodeId) ?? [];
const foundItems = filesIds.every((id) => sharedFiles.includes(id)); const foundItems = fileIds.every((id) => sharedFiles.includes(id));
if (foundItems) { if (!foundItems) {
return Promise.resolve(foundItems); const message = 'Not all files are shared yet';
} else { logger.error(message);
return Promise.reject(foundItems); throw new Error(message);
} }
}; };
return await Utils.retryCall(sharedFile); await Utils.retryCall(sharedFile);
} catch (error) { } catch (error) {
console.error(`SharedLinksApi waitForFilesToBeShared : catch : ${error}`); logger.error(`SharedLinksApi waitForFilesToBeShared : catch : ${error} - Wait timeout reached waiting for files to be shared`);
console.error(`\tWait timeout reached waiting for files to be shared`);
} }
} }
private async getSharedIdOfNode(fileId: string): Promise<string> { private async getSharedIdOfNode(fileId: string): Promise<string> {
try { const sharedLinksEntries = (await this.getSharedLinks())?.list?.entries ?? [];
const sharedLinksEntries = (await this.getSharedLinks())?.list.entries; const found = sharedLinksEntries.find((sharedLink) => sharedLink.entry.nodeId === fileId);
const found = sharedLinksEntries.find((sharedLink) => sharedLink.entry.nodeId === fileId); if (!found?.entry.id) {
return found?.entry.id; const message = `SharedLinksApi getSharedIdOfNode: no shared link found for node ${fileId}`;
} catch (error) { logger.error(message);
console.error(`SharedLinksApi getSharedIdOfNode : catch : `, error); throw new Error(message);
return null;
} }
return found.entry.id;
} }
async unshareFileById(fileId: string): Promise<void> { async unshareFileById(fileId: string): Promise<void> {
try { try {
const sharedId = await this.getSharedIdOfNode(fileId); const sharedId = await this.getSharedIdOfNode(fileId);
return this.apiService.share.deleteSharedLink(sharedId); await this.apiService.share.deleteSharedLink(sharedId);
} catch (error) { } catch (error) {
console.error(`SharedLinksApi unshareFileById : catch : `, error); logger.error(`SharedLinksApi unshareFileById : catch : ${error}`);
} }
} }
async waitForFilesToNotBeShared(filesIds: string[]): Promise<any> { async waitForFilesToNotBeShared(fileIds: string[]): Promise<void> {
try { try {
const sharedFile = async () => { const sharedFile = async () => {
const sharedFiles = (await this.getSharedLinks()).list.entries.map((link) => link.entry.nodeId); const sharedFiles = (await this.getSharedLinks()).list?.entries?.map((link) => link.entry.nodeId) ?? [];
const foundItems = filesIds.some((id) => { const foundItems = fileIds.some((id) => sharedFiles.includes(id));
return sharedFiles.includes(id);
});
if (foundItems) { if (foundItems) {
return Promise.reject(foundItems); const message = 'Some files are still shared';
} else { logger.error(message);
return Promise.resolve(foundItems); throw new Error(message);
} }
}; };
return await Utils.retryCall(sharedFile); await Utils.retryCall(sharedFile);
} catch (error) { } catch (error) {
console.error(`SharedLinksApi waitForFilesToNotBeShared : catch : ${error}`); logger.error(`SharedLinksApi waitForFilesToNotBeShared : catch : ${error} - Wait timeout reached waiting for files to no longer be shared`);
console.error(`\tWait timeout reached waiting for files to no longer be shared`);
} }
} }
} }
@@ -23,6 +23,7 @@
*/ */
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { logger } from '../utils';
import { import {
Site, Site,
SiteBodyCreate, SiteBodyCreate,
@@ -55,9 +56,11 @@ export class SitesApi {
} as SiteBodyCreate; } as SiteBodyCreate;
try { try {
return this.apiService.sites.createSite(site); return await this.apiService.sites.createSite(site);
} catch (error) { } catch (error) {
throw new Error(`SitesApi ${this.createSite.name}: ${error}`); const message = `SitesApi ${this.createSite.name}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -65,11 +68,15 @@ export class SitesApi {
try { try {
const id = (await this.apiService.sites.listSiteContainers(siteId)).list?.entries?.[0]?.entry?.id; const id = (await this.apiService.sites.listSiteContainers(siteId)).list?.entries?.[0]?.entry?.id;
if (!id) { if (!id) {
throw new Error(`Document library not found for site ${siteId}`); const message = `Document library not found for site ${siteId}`;
logger.error(message);
throw new Error(message);
} }
return id; return id;
} catch (error) { } catch (error) {
throw new Error(`Failed to get document library ID for site ${siteId}: ${error}`); const message = `Failed to get document library ID for site ${siteId}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -86,7 +93,7 @@ export class SitesApi {
} }
} }
} catch (error) { } catch (error) {
console.error(`${this.constructor.name} ${this.deleteSites.name}`, error); logger.error(`${this.constructor.name} ${this.deleteSites.name}: ${error}`);
} }
} }
@@ -96,9 +103,9 @@ export class SitesApi {
} as SiteMembershipBodyUpdate; } as SiteMembershipBodyUpdate;
try { try {
return this.apiService.sites.updateSiteMembership(siteId, userId, siteRole); return await this.apiService.sites.updateSiteMembership(siteId, userId, siteRole);
} catch (error) { } catch (error) {
console.error(`SitesApi updateSiteMember : catch : `, error); logger.error(`SitesApi updateSiteMember : catch : ${error}`);
return new SiteMemberEntry(); return new SiteMemberEntry();
} }
} }
@@ -127,7 +134,9 @@ export class SitesApi {
try { try {
return this.apiService.sites.createSiteMembershipRequestForPerson(personId, body); return this.apiService.sites.createSiteMembershipRequestForPerson(personId, body);
} catch (error) { } catch (error) {
throw new Error(`Failed to create site membership request for person ${personId} and site ${siteId}: ${error}`); const message = `Failed to create site membership request for person ${personId} and site ${siteId}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -135,7 +144,9 @@ export class SitesApi {
try { try {
return this.apiService.sites.approveSiteMembershipRequest(siteId, inviteeId); return this.apiService.sites.approveSiteMembershipRequest(siteId, inviteeId);
} catch (error) { } catch (error) {
throw new Error(`Failed to approve site membership request for invitee ${inviteeId} and site ${siteId}: ${error}`); const message = `Failed to approve site membership request for invitee ${inviteeId} and site ${siteId}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -145,7 +156,9 @@ export class SitesApi {
const requests = entries.map((e) => e.entry?.id).filter((id): id is string => !!id); const requests = entries.map((e) => e.entry?.id).filter((id): id is string => !!id);
return requests.includes(siteId); return requests.includes(siteId);
} catch (error) { } catch (error) {
throw new Error(`Failed to check site membership request for person ${personId} and site ${siteId}: ${error}`); const message = `Failed to check site membership request for person ${personId} and site ${siteId}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -153,7 +166,7 @@ export class SitesApi {
try { try {
return this.apiService.sites.deleteSiteMembership(siteId, userId); return this.apiService.sites.deleteSiteMembership(siteId, userId);
} catch (error) { } catch (error) {
console.error(`SitesApi deleteSiteMember : catch : `, error); logger.error(`SitesApi deleteSiteMember : catch : ${error}`);
} }
} }
@@ -161,7 +174,9 @@ export class SitesApi {
try { try {
return this.apiService.sites.getSite(siteId); return this.apiService.sites.getSite(siteId);
} catch (error) { } catch (error) {
throw new Error(`Failed to get site ${siteId}: ${error}`); const message = `Failed to get site ${siteId}: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
} }
@@ -50,26 +50,34 @@ export class TagsApi {
} else if ('list' in result) { } else if ('list' in result) {
const firstEntry = result.list?.entries?.[0]; const firstEntry = result.list?.entries?.[0];
if (!firstEntry) { if (!firstEntry) {
throw new Error(`createTags returned a paging result with no entries for tag "${tag}"`); const message = `createTags returned a paging result with no entries for tag "${tag}"`;
logger.error(message);
throw new Error(message);
} }
created = firstEntry; created = firstEntry;
} else { } else {
throw new Error(`createTags returned an unexpected response format for tag "${tag}"`); const message = `createTags returned an unexpected response format for tag "${tag}"`;
logger.error(message);
throw new Error(message);
} }
logger.info(`Tag created: "${created.entry.tag}" (id: ${created.entry.id})`); logger.info(`Tag created: "${created.entry.tag}" (id: ${created.entry.id})`);
results.push(created); results.push(created);
} }
return results; return results;
} catch (error) { } catch (error) {
throw new Error(`Failed to create tags: ${error}`); const message = `Failed to create tags: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
async assignTagToNode(nodeId: string, tag: TagBody): Promise<TagEntry> { async assignTagToNode(nodeId: string, tag: TagBody): Promise<TagEntry> {
try { try {
return this.apiService.tagsApi.assignTagToNode(nodeId, tag); return await this.apiService.tagsApi.assignTagToNode(nodeId, tag);
} catch (error) { } catch (error) {
throw new Error(`Failed to assign tag to node: ${error}`); const message = `Failed to assign tag to node: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -81,7 +89,9 @@ export class TagsApi {
logger.info(`Tag deleted: ${tagLabel}(id: ${id})`); logger.info(`Tag deleted: ${tagLabel}(id: ${id})`);
} }
} catch (error) { } catch (error) {
throw new Error(`Failed to delete tags: ${error}`); const message = `Failed to delete tags: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -89,7 +99,9 @@ export class TagsApi {
try { try {
return this.apiService.tagsApi.listTagsForNode(nodeId); return this.apiService.tagsApi.listTagsForNode(nodeId);
} catch (error) { } catch (error) {
throw new Error(`Failed to list tags for node: ${error}`); const message = `Failed to list tags for node: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -97,7 +109,9 @@ export class TagsApi {
try { try {
return this.apiService.tagsApi.listTags(params); return this.apiService.tagsApi.listTags(params);
} catch (error) { } catch (error) {
throw new Error(`Failed to list tags: ${error}`); const message = `Failed to list tags: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
@@ -107,7 +121,9 @@ export class TagsApi {
const tags = response.list?.entries.map((entry) => entry.entry) || []; const tags = response.list?.entries.map((entry) => entry.entry) || [];
await this.deleteTags(...tags); await this.deleteTags(...tags);
} catch (error) { } catch (error) {
throw new Error(`Failed to delete tags by tag name: ${error}`); const message = `Failed to delete tags by tag name: ${error}`;
logger.error(message);
throw new Error(message);
} }
} }
} }
@@ -23,6 +23,7 @@
*/ */
import { ApiClientFactory } from './api-client-factory'; import { ApiClientFactory } from './api-client-factory';
import { logger } from '../utils';
export class TrashcanApi { export class TrashcanApi {
private readonly apiService = new ApiClientFactory(); private readonly apiService = new ApiClientFactory();
@@ -50,7 +51,7 @@ export class TrashcanApi {
} }
} }
} catch (error) { } catch (error) {
console.error('User Actions - emptyTrashcan failed : ', error); logger.error(`User Actions - emptyTrashcan failed: ${error}`);
} }
} }
} }