[MNT-24575] Added APIS and models folder information dialog (#10460)

* [MNT-24575] Added APIs folder information

* [MNT-24575] Added models

* [MNT-24575] Added constructors to model

* [MNT-24575] Added documentation

* [MNT-24575] Added documentation

* [MNT-24575] Addressed Code review comments

* [MNT-24575] Added unit tests. Fixed typo

* [MNT-24575] Fixed accesibility issue in DialogComponent. Fixed typo in enum

* [MNT-24575] Fixed typo, incorrect ACS version, and incorrect imports

* [MNT-24575] Added unit tests to js-api

* Empty commit to trigger GHA

* [ci:force] Empty force commit to trigger GHA

* [MNT-24575] Fixed linting issue

* [MNT-24575] Removed redundant *

* [ci:force] Empty force commit to trigger GHA
This commit is contained in:
swapnil-verma-gl 2025-01-07 15:21:44 +05:30 committed by GitHub
parent 2a1691836e
commit 4787470707
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 503 additions and 2 deletions

View File

@ -89,6 +89,15 @@ Accesses and manipulates ACS document nodes using their node IDs.
- _nodeId:_ `string` - ID of the target node
- _options:_ `{ includeSource?: boolean; } & NodesIncludeQuery & ContentPagingQuery` - Optional parameters supported by JS-API
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`Hold[]`](../../../lib/js-api/src/api/gs-core-rest-api/docs/Hold.md)`>` - List of assigned holds
- **initiateFolderSizeCalculation**(nodeId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`JobIdBodyEntry`](../../../lib/js-api/src/api/content-rest-api/docs/NodesApi.md#jobidbodyentry)`>`<br/>
Initiate a new request to calculate folder size.
- _nodeId:_ `string` - ID of the target node
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`JobIdBodyEntry`](../../../lib/js-api/src/api/content-rest-api/docs/NodesApi.md#jobidbodyentry)`>` - Job id which can be used to track request status
- **getFolderSizeInfo**(nodeId: `string`, jobId: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`SizeDetailsEntry`](../../../lib/js-api/src/api/content-rest-api/docs/NodesApi.md#sizedetailsentry)`>`<br/>
Gets the size of a folder.
- _nodeId:_ `string` - ID of the target node
- _jobId:_ `string` - ID of the job started by the `initiateFolderSizeCalculation` request
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`SizeDetailsEntry`](../../../lib/js-api/src/api/content-rest-api/docs/NodesApi.md#sizedetailsentry)`>` - Details of the folder
## Details

View File

@ -0,0 +1,73 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CoreTestingModule, RedirectAuthService } from '@alfresco/adf-core';
import { EMPTY, firstValueFrom, of } from 'rxjs';
import { JobIdBodyEntry, SizeDetails, SizeDetailsEntry } from '@alfresco/js-api';
import { NodesApiService } from './nodes-api.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { AlfrescoApiServiceMock } from '../../mock/alfresco-api.service.mock';
const fakeInitiateFolderSizeResponse: JobIdBodyEntry = {
entry: {
jobId: 'fake-job-id'
}
};
const fakeFolderSizeResponse: SizeDetailsEntry = {
entry: {
jobId: 'fake-job-id',
calculatedAt: new Date().toString(),
sizeInBytes: '1234',
numberOfFiles: 1,
status: SizeDetails.StatusEnum.COMPLETE,
id: 'fake-id'
}
};
describe('NodesApiService', () => {
let nodesApiService: NodesApiService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, CoreTestingModule],
providers: [
NodesApiService,
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: RedirectAuthService, useValue: { onLogin: EMPTY, onTokenReceived: of(), init: () => {} } }
]
});
nodesApiService = TestBed.inject(NodesApiService);
});
it('should call initiateFolderSizeCalculation api with nodeId parameter', async () => {
spyOn(nodesApiService.nodesApi, 'initiateFolderSizeCalculation').and.returnValue(Promise.resolve(fakeInitiateFolderSizeResponse));
await firstValueFrom(nodesApiService.initiateFolderSizeCalculation('fake-node-id'));
expect(nodesApiService.nodesApi.initiateFolderSizeCalculation).toHaveBeenCalledWith('fake-node-id');
});
it('should call getFolderSizeInfo api with nodeId and jobId parameter', async () => {
spyOn(nodesApiService.nodesApi, 'getFolderSizeInfo').and.returnValue(Promise.resolve(fakeFolderSizeResponse));
await firstValueFrom(nodesApiService.getFolderSizeInfo('fake-node-id', 'fake-job-id'));
expect(nodesApiService.nodesApi.getFolderSizeInfo).toHaveBeenCalledWith('fake-node-id', 'fake-job-id');
});
});

View File

@ -16,7 +16,18 @@
*/
import { UserPreferencesService } from '@alfresco/adf-core';
import { ContentPagingQuery, Node, NodeAssignedHold, NodeEntry, NodePaging, NodesApi, NodesIncludeQuery, TrashcanApi } from '@alfresco/js-api';
import {
ContentPagingQuery,
Node,
NodeAssignedHold,
NodeEntry,
NodePaging,
NodesApi,
NodesIncludeQuery,
TrashcanApi,
SizeDetailsEntry,
JobIdBodyEntry
} from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { from, Observable, Subject, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@ -246,6 +257,27 @@ export class NodesApiService {
return this.createNodeInsideRoot(name || this.randomNodeName(), nodeType, properties, path);
}
/**
* Initiate a new request to calculate folder size.
*
* @param nodeId Node Id
* @returns The job id which can be used to track request status
*/
initiateFolderSizeCalculation(nodeId: string): Observable<JobIdBodyEntry> {
return from(this.nodesApi.initiateFolderSizeCalculation(nodeId));
}
/**
* Gets the size of a folder.
*
* @param nodeId Node Id
* @param jobId Job Id
* @returns Folder details
*/
getFolderSizeInfo(nodeId: string, jobId: string): Observable<SizeDetailsEntry> {
return from(this.nodesApi.getFolderSizeInfo(nodeId, jobId));
}
private randomNodeName(): string {
return `node_${Date.now()}`;
}

View File

@ -22,6 +22,7 @@
*ngIf="!isCloseButtonHidden"
mat-icon-button
mat-dialog-close
title="{{'CLOSE' | translate }}"
data-automation-id="adf-dialog-close-button"
>
<mat-icon>close</mat-icon>

View File

@ -28,7 +28,9 @@ import {
NodeBodyMove,
NodeBodyUpdate,
NodeChildAssociationPaging,
NodeEntry
NodeEntry,
SizeDetailsEntry,
JobIdBodyEntry
} from '../model';
import { BaseApi } from './base.api';
import { throwIfNotDefined } from '../../../assert';
@ -960,4 +962,50 @@ export class NodesApi extends BaseApi {
returnType: DirectAccessUrlEntry
});
}
/**
* Initiate a new request to calculate folder size.
*
* **Note:** this endpoint is available in Alfresco 25.1.0 and newer versions.
* @param nodeId Node Id
* @returns The job id which can be used to track request status
*/
initiateFolderSizeCalculation(nodeId: string): Promise<JobIdBodyEntry> {
throwIfNotDefined(nodeId, 'nodeId');
const pathParams = {
nodeId
};
return this.post({
path: '/nodes/{nodeId}/size-details',
pathParams,
returnType: JobIdBodyEntry
});
}
/**
* Gets the size of a folder.
*
* **Note:** this endpoint is available in Alfresco 25.1.0 and newer versions.
* @param nodeId Node Id
* @param jobId Job Id
* @returns Folder details
*/
getFolderSizeInfo(nodeId: string, jobId: string): Promise<SizeDetailsEntry> {
throwIfNotDefined(nodeId, 'nodeId');
throwIfNotDefined(jobId, 'jobId');
const pathParams = {
nodeId,
jobId
};
return this.get({
path: 'nodes/{nodeId}/size-details/{jobId}',
pathParams,
returnType: SizeDetailsEntry
});
}
}

View File

@ -25,6 +25,8 @@ All URIs are relative to *https://localhost/alfresco/api/-default-/public/alfres
| [unlockNode](#unlockNode) | **POST** /nodes/{nodeId}/unlock | Unlock a node |
| [updateNode](#updateNode) | **PUT** /nodes/{nodeId} | Update a node |
| [updateNodeContent](#updateNodeContent) | **PUT** /nodes/{nodeId}/content | Update node content |
| [initiateFolderSizeCalculation](#initiateFolderSizeCalculation) | **POST** /nodes/{nodeId}/size-details | Initiate a new request to calculate folder size |
| [getFolderSizeInfo](#getFolderSizeInfo) | **GET** /nodes/{nodeId}/size-details/{jobId} | Gets the details of a folder |
## copyNode
@ -1196,6 +1198,67 @@ nodesApi.updateNodeContent(`<nodeId>`, contentBodyUpdate, opts).then((data) => {
console.log('API called successfully. Returned data: ' + data);
});
```
## initiateFolderSizeCalculation
Initiates a new request to calculate folder size.
> this endpoint is available in **Alfresco 25.1.0** and newer versions.
Initiates a request to calculate the size of the node with identifier **nodeId**.
**Parameters**
| Name | Type | Description |
|-----------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **nodeId** | string | The identifier of a node. |
**Return type**: [JobIdBodyEntry](#JobIdBodyEntry)
**Example**
```javascript
import {AlfrescoApi, NodesApi} from '@alfresco/js-api';
const alfrescoApi = new AlfrescoApi(/*..*/);
const nodesApi = new NodesApi(alfrescoApi);
const contentBodyUpdate = {};
const opts = {};
nodesApi.initiateFolderSizeCalculation(`<nodeId>`).then((data) => {
console.log('API called successfully. Returned data: ' + data);
});
```
## getFolderSizeInfo
Gets the size details of a folder
> this endpoint is available in **Alfresco 25.1.0** and newer versions.
Fetches the size details of folder with the identifier **nodeId**
**Parameters**
| Name | Type | Description |
|------------|----------|------------------------------------------------------------------------------------|
| **nodeId** | string | The identifier of a node. |
| **jobId** | string | The identifier for the job which is calculating the currently selected node's size |
**Return type**: [SizeDetailsEntry](#SizeDetailsEntry)
**Example**
```javascript
import {AlfrescoApi, NodesApi} from '@alfresco/js-api';
const alfrescoApi = new AlfrescoApi(/*..*/);
const nodesApi = new NodesApi(alfrescoApi);
const contentBodyUpdate = {};
const opts = {};
nodesApi.getFolderSizeInfo(`<nodeId>`, `<jobId>`).then((data) => {
console.log('API called successfully. Returned data: ' + data);
});
```
# Models
@ -1409,6 +1472,39 @@ nodesApi.updateNodeContent(`<nodeId>`, contentBodyUpdate, opts).then((data) => {
| isInheritanceEnabled | boolean |
| locallySet | [PermissionElement[]](PermissionElement.md) |
# JobIdBodyEntry
**Properties**
| Name | Type |
|-----------|-------------------------|
| **entry** | [JobIdBody](#JobIdBody) |
# JobIdBody
**Properties**
| Name | Type | Description | Notes |
|-----------|--------|---------------------------------------------------------------------------------------|-------------------|
| **jobId** | string | Id of the job that can be used to track the status of folder size calculation request | [default to null] |
# SizeDetailsEntry
**Properties**
| Name | Type |
|-----------|-----------------------------|
| **entry** | [SizeDetails](#SizeDetails) |
# SizeDetails
**Properties**
| Name | Type | Description | Notes |
|-------------------|--------|-----------------------------------------------------------------------------|------------------|
| **id** | string | Unique alphanumeric id unique to this response request | [default to null]|
| **sizeInBytes** | string | Size of the folder in bytes | [default to null]|
| **calculatedAt** | string | Timestamp of when the folder size was calculated | [default to null]|
| **numberOfFiles** | number | Number of files present within the folder | [default to null]|
| **status** | string | Status of the request. Can be 'NOT-INITIATED', 'IN-PROGRESS' or 'COMPLETED' | [default to null]|
| **jobId** | string | The job ID which was used to track down the folder details | [default to null]|

View File

@ -211,3 +211,7 @@ export * from './versionPagingList';
export * from './deletedNode';
export * from './nodeAssociation';
export * from './nodeChildAssociation';
export * from './sizeDetails';
export * from './sizeDetailsEntry';
export * from './jobIdBody';
export * from './jobIdBodyEntry';

View File

@ -0,0 +1,24 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class JobIdBody {
jobId: string;
constructor(jobIdBody: JobIdBody) {
this.jobId = jobIdBody.jobId;
}
}

View File

@ -0,0 +1,29 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { JobIdBody } from './jobIdBody';
export class JobIdBodyEntry {
entry: JobIdBody;
constructor(input?: Partial<JobIdBodyEntry>) {
if (input) {
Object.assign(this, input);
this.entry = input.entry ? new JobIdBody(input.entry) : undefined;
}
}
}

View File

@ -0,0 +1,42 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class SizeDetails {
id: string;
sizeInBytes: string;
calculatedAt: string;
numberOfFiles: number;
status: SizeDetails.StatusEnum;
jobId: string;
constructor(entry: SizeDetails) {
this.id = entry.id;
this.sizeInBytes = entry.sizeInBytes;
this.calculatedAt = entry.calculatedAt;
this.numberOfFiles = entry.numberOfFiles;
this.status = entry.status;
this.jobId = entry.jobId;
}
}
export namespace SizeDetails {
export type StatusEnum = 'IN-PROGRESS' | 'COMPLETED' | 'NOT-INITIATED';
export const StatusEnum = {
IN_PROGRESS: 'IN-PROGRESS' as StatusEnum,
COMPLETE: 'COMPLETED' as StatusEnum,
NOT_INITIATED: 'NOT-INITIATED' as StatusEnum
};
}

View File

@ -0,0 +1,29 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SizeDetails } from './sizeDetails';
export class SizeDetailsEntry {
entry: SizeDetails;
constructor(input?: Partial<SizeDetailsEntry>) {
if (input) {
Object.assign(this, input);
this.entry = input.entry ? new SizeDetails(input.entry) : undefined;
}
}
}

View File

@ -150,4 +150,59 @@ describe('Node', () => {
);
});
});
describe('FolderInformation', () => {
it('should return jobId on initiateFolderSizeCalculation API call if everything is ok', (done) => {
nodeMock.post200ResponseInitiateFolderSizeCalculation();
nodesApi.initiateFolderSizeCalculation('b4cff62a-664d-4d45-9302-98723eac1319').then((response) => {
assert.equal(response.entry.jobId, '5ade426e-8a04-4d50-9e42-6e8a041d50f3');
done();
});
});
it('should return 404 error on initiateFolderSizeCalculation API call if nodeId is not found', (done) => {
nodeMock.post404NodeIdNotFound();
nodesApi.initiateFolderSizeCalculation('b4cff62a-664d-4d45-9302-98723eac1319').then(
() => {},
(err) => {
const { error } = JSON.parse(err.response.text);
assert.equal(error.statusCode, 404);
assert.equal(error.errorKey, 'framework.exception.EntityNotFound');
assert.equal(error.briefSummary, '11207522 The entity with id: b4cff62a-664d-4d45-9302-98723eac1319 was not found');
done();
}
);
});
it('should return size details on getFolderSizeInfo API call if everything is ok', (done) => {
nodeMock.get200ResponseGetFolderSizeInfo();
nodesApi.getFolderSizeInfo('b4cff62a-664d-4d45-9302-98723eac1319', '5ade426e-8a04-4d50-9e42-6e8a041d50f3').then((response) => {
assert.equal(response.entry.id, '32e522f1-1f28-4ea3-a522-f11f284ea397');
assert.equal(response.entry.jobId, '5ade426e-8a04-4d50-9e42-6e8a041d50f3');
assert.equal(response.entry.sizeInBytes, 2689);
assert.equal(response.entry.numberOfFiles, 100);
assert.equal(response.entry.calculatedAt, '2024-12-20T12:02:23.989+0000');
assert.equal(response.entry.status, 'COMPLETED');
done();
});
});
it('should return 404 error on getFolderSizeInfo API call if jobId is not found', (done) => {
nodeMock.get404JobIdNotFound();
nodesApi.getFolderSizeInfo('b4cff62a-664d-4d45-9302-98723eac1319', '5ade426e-8a04-4d50-9e42-6e8a041d50f3').then(
() => {},
(err) => {
const { error } = JSON.parse(err.response.text);
assert.equal(error.statusCode, 404);
assert.equal(error.errorKey, 'jobId does not exist');
assert.equal(error.briefSummary, '11207212 jobId does not exist');
done();
}
);
});
});
});

View File

@ -230,4 +230,63 @@ export class NodeMock extends BaseMock {
}
});
}
post200ResponseInitiateFolderSizeCalculation(): void {
nock(this.host, { encodedQueryParams: true })
.post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details')
.reply(200, {
entry: {
jobId: '5ade426e-8a04-4d50-9e42-6e8a041d50f3'
}
});
}
post404NodeIdNotFound(): void {
nock(this.host, { encodedQueryParams: true })
.post('/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details')
.reply(404, {
error: {
errorKey: 'framework.exception.EntityNotFound',
statusCode: 404,
briefSummary: '11207522 The entity with id: b4cff62a-664d-4d45-9302-98723eac1319 was not found',
stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions',
descriptionURL: 'https://api-explorer.alfresco.com',
logId: 'fafaf3c9-4e23-412b-baf3-c94e23912be5'
}
});
}
get200ResponseGetFolderSizeInfo(): void {
nock(this.host, { encodedQueryParams: true })
.get(
'/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details/5ade426e-8a04-4d50-9e42-6e8a041d50f3'
)
.reply(200, {
entry: {
numberOfFiles: 100,
jobId: '5ade426e-8a04-4d50-9e42-6e8a041d50f3',
sizeInBytes: 2689,
id: '32e522f1-1f28-4ea3-a522-f11f284ea397',
calculatedAt: '2024-12-20T12:02:23.989+0000',
status: 'COMPLETED'
}
});
}
get404JobIdNotFound(): void {
nock(this.host, { encodedQueryParams: true })
.get(
'/alfresco/api/-default-/public/alfresco/versions/1/nodes/b4cff62a-664d-4d45-9302-98723eac1319/size-details/5ade426e-8a04-4d50-9e42-6e8a041d50f3'
)
.reply(404, {
error: {
errorKey: 'jobId does not exist',
statusCode: 404,
briefSummary: '11207212 jobId does not exist',
stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions',
descriptionURL: 'https://api-explorer.alfresco.com',
logId: 'a98180c0-b1c0-48cb-8180-c0b1c0f8cba8'
}
});
}
}