AAE-45003 Extend task cloud services (#11850)

This commit is contained in:
Fabian Kindgen
2026-05-06 09:45:29 +02:00
committed by GitHub
parent 2649cdea2d
commit a73b100fae
6 changed files with 152 additions and 7 deletions
@@ -84,6 +84,18 @@ export class BaseCloudService {
);
}
protected getWithBody<T, R>(url: string, data?: T, queryParams?: any): Observable<R> {
return from(
this.callApi<R>(url, {
...this.defaultParams,
path: url,
httpMethod: 'GET',
bodyParam: data,
queryParams
})
);
}
protected callApi<T>(url: string, params: RequestOptions): Promise<T> {
return this.adfHttpClient.request(url, params);
}
@@ -34,8 +34,18 @@ export interface TaskListCloudServiceInterface {
* Retrieves a list of tasks using an object with optional query properties.
*
* @param requestNode Query object
* @param queryUrl Query url
* @param queryUrl Query url. If empty, query service will be called.
* @returns List of tasks
*/
fetchTaskList(requestNode: TaskListRequestModel, queryUrl?: string): Observable<any>;
/**
* Available from Activiti version 8.7.0 onwards.
* Retrieves a list of tasks using an object with optional query properties.
* Calls runtime bundle service.
*
* @param requestNode Query object
* @returns List of tasks
*/
fetchTaskList_UsingRuntimeBundleService(requestNode: TaskListRequestModel): Observable<any>;
}
@@ -26,6 +26,7 @@ import {
} from '../task-header/mocks/task-details-cloud.mock';
import { IdentityUserService } from '../../people/services/identity-user.service';
import { AdfHttpClient } from '@alfresco/adf-core/api';
import { TASK_COMPLETED_STATE, TASK_CREATED_STATE, TASK_ASSIGNED_STATE } from '../models/task-details-cloud.model';
const fakeTaskDetailsCloud = {
entry: {
@@ -478,4 +479,46 @@ describe('Task Cloud Service', () => {
}
);
});
describe('wasTaskCompletedByCurrentUser', () => {
it('should return true when task was completed by current user', () => {
const completedTaskByCurrentUser = {
...assignedTaskDetailsCloudMock,
status: TASK_COMPLETED_STATE,
assignee: 'AssignedTaskUser'
};
const result = service.wasTaskCompletedByCurrentUser(completedTaskByCurrentUser);
expect(result).toBe(true);
});
it('should return false when task was completed but not by current user', () => {
const completedTaskByDifferentUser = {
...assignedTaskDetailsCloudMock,
status: TASK_COMPLETED_STATE,
assignee: 'DifferentUser'
};
const result = service.wasTaskCompletedByCurrentUser(completedTaskByDifferentUser);
expect(result).toBe(false);
});
it('should return false when task is not completed', () => {
const uncompletedTask = {
...assignedTaskDetailsCloudMock,
status: TASK_ASSIGNED_STATE,
assignee: 'AssignedTaskUser'
};
const result = service.wasTaskCompletedByCurrentUser(uncompletedTask);
expect(result).toBe(false);
});
it('should return false when task is in other states', () => {
const createdTask = {
...assignedTaskDetailsCloudMock,
status: TASK_CREATED_STATE,
assignee: 'AssignedTaskUser'
};
const result = service.wasTaskCompletedByCurrentUser(createdTask);
expect(result).toBe(false);
});
});
});
@@ -25,7 +25,8 @@ import {
TASK_ASSIGNED_STATE,
TASK_CLAIM_PERMISSION,
TASK_CREATED_STATE,
TASK_RELEASE_PERMISSION
TASK_RELEASE_PERMISSION,
TASK_COMPLETED_STATE
} from '../models/task-details-cloud.model';
import { BaseCloudService } from '../../services/base-cloud.service';
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
@@ -67,7 +68,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Boolean value if the task can be completed
*/
canCompleteTask(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee);
return taskDetails?.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee);
}
/**
@@ -77,7 +78,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Boolean value if the task is editable
*/
isTaskEditable(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails && taskDetails.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails.assignee);
return this.canCompleteTask(taskDetails);
}
isAssigneePropertyClickable(
@@ -93,6 +94,16 @@ export class TaskCloudService extends BaseCloudService {
return isClickable;
}
/**
* Validates if a task was completed by the current user.
*
* @param taskDetails task details object
* @returns Boolean value if the task was completed by the current user
*/
wasTaskCompletedByCurrentUser(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails?.status === TASK_COMPLETED_STATE && this.isAssignedToMe(taskDetails.assignee);
}
/**
* Validate if a task can be claimed.
*
@@ -169,11 +180,12 @@ export class TaskCloudService extends BaseCloudService {
*
* @param appName Name of the app
* @param taskId ID of the task whose details you want
* @param service The service to call. Either Query Service or Runtime Bundle Service.
* @returns Task details
*/
getTaskById(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
getTaskById(appName: string, taskId: string, service: 'query' | 'rb' = 'query'): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}`;
const queryUrl = `${this.getBasePath(appName)}/${service}/v1/tasks/${taskId}`;
return this.get(queryUrl).pipe(map((res: any) => res.entry));
} else {
@@ -158,6 +158,49 @@ describe('TaskListCloudService', () => {
expect(res).toBe('Appname not configured');
});
});
describe('fetchTaskList_UsingRuntimeBundleService', () => {
it('should call runtime bundle endpoint using GET', async () => {
const taskRequest = {
appName: 'fakeName',
pagination: { skipCount: 0, maxItems: 20 }
} as TaskListRequestModel;
requestSpy.and.callFake(returnCallQueryParameters);
const res = await firstValueFrom(service.fetchTaskList_UsingRuntimeBundleService(taskRequest));
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.skipCount).toBe(0);
expect(res.maxItems).toBe(20);
expect(requestSpy.calls.mostRecent().args[0]).toContain('/fakeName/rb/v1/tasks');
expect(requestSpy.calls.mostRecent().args[1].httpMethod).toBe('GET');
});
it('should use default pagination values when pagination is not specified', async () => {
const taskRequest = {
appName: 'fakeName'
} as TaskListRequestModel;
requestSpy.and.callFake(returnCallQueryParameters);
const res = await firstValueFrom(service.fetchTaskList_UsingRuntimeBundleService(taskRequest));
expect(res.skipCount).toBe(0);
expect(res.maxItems).toBe(25);
});
it('should return an error when app name is not specified', async () => {
const taskRequest = { appName: null } as TaskListRequestModel;
requestSpy.and.callFake(returnCallUrl);
const res = await firstValueFrom(
service.fetchTaskList_UsingRuntimeBundleService(taskRequest).pipe(catchError((error) => of(error.message)))
);
expect(res).toBe('Appname not configured');
});
});
describe('getTaskListCount', () => {
it('should concat the app name to the request url', async () => {
const taskRequest = {
@@ -62,7 +62,7 @@ export class TaskListCloudService extends BaseCloudService implements TaskListCl
* Retrieves a list of tasks using an object with optional query properties.
*
* @param requestNode Query object
* @param queryUrl Query url
* @param queryUrl Query url. If empty, query service will be called.
* @returns List of tasks
*/
fetchTaskList(requestNode: TaskListRequestModel, queryUrl?: string): Observable<any> {
@@ -90,6 +90,31 @@ export class TaskListCloudService extends BaseCloudService implements TaskListCl
);
}
fetchTaskList_UsingRuntimeBundleService(requestNode: TaskListRequestModel): Observable<any> {
if (!requestNode?.appName) {
return throwError(() => new Error('Appname not configured'));
}
const url = `${this.getBasePath(requestNode.appName)}/rb/v1/tasks`;
const queryParams = {
maxItems: requestNode.pagination?.maxItems || 25,
skipCount: requestNode.pagination?.skipCount || 0
};
const queryData = this.buildQueryData(requestNode);
return this.getWithBody<any, TaskCloudNodePaging>(url, queryData, queryParams).pipe(
map((response) => {
const entries = response.list?.entries;
if (entries) {
response.list.entries = entries.map((entryData) => entryData.entry) as any;
}
return response;
})
);
}
getTaskListCounter(requestNode: TaskListRequestModel): Observable<number> {
if (!requestNode.appName) {
return throwError(() => new Error('Appname not configured'));