AAE-47634 Fix block task claim checking status on Runtime Bundle (#12009)

* AAE-47634 Add taskDetailsSource input to read task from

* AAE-47634 Fall back to Query strategy when taskDetailsSource is unsupported

* AAE-47634 Reset loading state when task details request fails

* AAE-47634 Reload task on taskDetailsSource change
This commit is contained in:
Amedeo Lepore
2026-06-26 17:10:22 +00:00
committed by GitHub
parent d2d6696c05
commit bd7f393db9
9 changed files with 512 additions and 3 deletions
@@ -578,4 +578,49 @@ describe('Task Cloud Service', () => {
expect(result).toBe(false);
});
});
describe('canClaimTaskByState', () => {
it('should return true for a created, non-standalone task without relying on permissions', () => {
const task = { ...createdTaskDetailsCloudMock, status: TASK_CREATED_STATE, standalone: false, permissions: undefined };
expect(service.canClaimTaskByState(task)).toBe(true);
});
it('should return false when the task is not in the created state', () => {
const task = { ...createdTaskDetailsCloudMock, status: TASK_ASSIGNED_STATE, standalone: false };
expect(service.canClaimTaskByState(task)).toBe(false);
});
it('should return false when the task is standalone', () => {
const task = { ...createdTaskDetailsCloudMock, status: TASK_CREATED_STATE, standalone: true };
expect(service.canClaimTaskByState(task)).toBe(false);
});
});
describe('canUnclaimTaskByState', () => {
it('should return true for an assigned, non-standalone task assigned to the current user without relying on permissions', () => {
const task = {
...assignedTaskDetailsCloudMock,
status: TASK_ASSIGNED_STATE,
assignee: 'AssignedTaskUser',
standalone: false,
permissions: undefined
};
expect(service.canUnclaimTaskByState(task)).toBe(true);
});
it('should return false when the task is assigned to a different user', () => {
const task = { ...assignedTaskDetailsCloudMock, status: TASK_ASSIGNED_STATE, assignee: 'DifferentUser', standalone: false };
expect(service.canUnclaimTaskByState(task)).toBe(false);
});
it('should return false when the task is not in the assigned state', () => {
const task = { ...assignedTaskDetailsCloudMock, status: TASK_CREATED_STATE, assignee: 'AssignedTaskUser', standalone: false };
expect(service.canUnclaimTaskByState(task)).toBe(false);
});
it('should return false when the task is standalone', () => {
const task = { ...assignedTaskDetailsCloudMock, status: TASK_ASSIGNED_STATE, assignee: 'AssignedTaskUser', standalone: true };
expect(service.canUnclaimTaskByState(task)).toBe(false);
});
});
});
@@ -130,6 +130,32 @@ export class TaskCloudService extends BaseCloudService {
);
}
/**
* Validate if a task can be claimed based solely on its state, ignoring candidate
* `permissions`. Intended for callers reading the task from the Runtime Bundle, whose
* responses do not include the `permissions` evaluated by the (eventually consistent)
* Query Service.
*
* @param taskDetails task details object
* @returns Boolean value if the task can be claimed
*/
canClaimTaskByState(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails?.status === TASK_CREATED_STATE && !taskDetails?.standalone;
}
/**
* Validate if a task can be unclaimed based solely on its state and assignee, ignoring
* candidate `permissions`. Intended for callers reading the task from the Runtime Bundle,
* whose responses do not include the `permissions` evaluated by the (eventually consistent)
* Query Service.
*
* @param taskDetails task details object
* @returns Boolean value if the task can be unclaimed
*/
canUnclaimTaskByState(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails?.status === TASK_ASSIGNED_STATE && this.isAssignedToMe(taskDetails?.assignee) && !taskDetails?.standalone;
}
/**
* Returns the next recommended task to process.
*
@@ -0,0 +1,61 @@
/*!
* @license
* Copyright © 2005-2025 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 { of } from 'rxjs';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../../models/task-details-cloud.model';
import { QueryTaskDetailsSourceStrategy } from './query-task-details-source.strategy';
describe('QueryTaskDetailsSourceStrategy', () => {
let strategy: QueryTaskDetailsSourceStrategy;
let taskCloudService: jasmine.SpyObj<TaskCloudService>;
const taskDetails = { id: 'task-1' } as TaskDetailsCloudModel;
beforeEach(() => {
taskCloudService = jasmine.createSpyObj('TaskCloudService', ['getTaskById', 'canClaimTask', 'canUnclaimTask']);
TestBed.configureTestingModule({
providers: [QueryTaskDetailsSourceStrategy, { provide: TaskCloudService, useValue: taskCloudService }]
});
strategy = TestBed.inject(QueryTaskDetailsSourceStrategy);
});
it('should fetch the task details from the Query Service', () => {
taskCloudService.getTaskById.and.returnValue(of(taskDetails));
strategy.getTaskDetails$('app', 'task-1').subscribe();
expect(taskCloudService.getTaskById).toHaveBeenCalledWith('app', 'task-1');
});
it('should delegate claim eligibility to canClaimTask', () => {
taskCloudService.canClaimTask.and.returnValue(true);
expect(strategy.canClaim(taskDetails)).toBe(true);
expect(taskCloudService.canClaimTask).toHaveBeenCalledWith(taskDetails);
});
it('should delegate unclaim eligibility to canUnclaimTask', () => {
taskCloudService.canUnclaimTask.and.returnValue(true);
expect(strategy.canUnclaim(taskDetails)).toBe(true);
expect(taskCloudService.canUnclaimTask).toHaveBeenCalledWith(taskDetails);
});
});
@@ -0,0 +1,43 @@
/*!
* @license
* Copyright © 2005-2025 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 { inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../../models/task-details-cloud.model';
import { TaskDetailsSourceStrategy } from './task-details-source.strategy';
/**
* Reads task details from the Query Service and evaluates claim/unclaim eligibility from
* the candidate `permissions` it returns.
*/
@Injectable({ providedIn: 'root' })
export class QueryTaskDetailsSourceStrategy implements TaskDetailsSourceStrategy {
private readonly taskCloudService = inject(TaskCloudService);
getTaskDetails$(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
return this.taskCloudService.getTaskById(appName, taskId);
}
canClaim(taskDetails: TaskDetailsCloudModel): boolean {
return this.taskCloudService.canClaimTask(taskDetails);
}
canUnclaim(taskDetails: TaskDetailsCloudModel): boolean {
return this.taskCloudService.canUnclaimTask(taskDetails);
}
}
@@ -0,0 +1,107 @@
/*!
* @license
* Copyright © 2005-2025 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 { of, throwError } from 'rxjs';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../../models/task-details-cloud.model';
import { RuntimeBundleTaskDetailsSourceStrategy } from './runtime-bundle-task-details-source.strategy';
describe('RuntimeBundleTaskDetailsSourceStrategy', () => {
let strategy: RuntimeBundleTaskDetailsSourceStrategy;
let taskCloudService: jasmine.SpyObj<TaskCloudService>;
const taskDetails = { id: 'task-1' } as TaskDetailsCloudModel;
beforeEach(() => {
taskCloudService = jasmine.createSpyObj('TaskCloudService', [
'getTaskById',
'canClaimTask',
'canClaimTaskByState',
'canUnclaimTask',
'canUnclaimTaskByState'
]);
TestBed.configureTestingModule({
providers: [RuntimeBundleTaskDetailsSourceStrategy, { provide: TaskCloudService, useValue: taskCloudService }]
});
strategy = TestBed.inject(RuntimeBundleTaskDetailsSourceStrategy);
});
it('should fetch the task details from the Runtime Bundle', () => {
taskCloudService.getTaskById.and.returnValue(of(taskDetails));
strategy.getTaskDetails$('app', 'task-1').subscribe();
expect(taskCloudService.getTaskById).toHaveBeenCalledWith('app', 'task-1', 'rb');
});
it('should fall back to the Query Service when the Runtime Bundle returns 404 for a terminal task', () => {
taskCloudService.getTaskById.withArgs('app', 'task-1', 'rb').and.returnValue(throwError(() => ({ status: 404 })));
taskCloudService.getTaskById.withArgs('app', 'task-1', 'query').and.returnValue(of(taskDetails));
let result: TaskDetailsCloudModel;
strategy.getTaskDetails$('app', 'task-1').subscribe((task) => (result = task));
expect(taskCloudService.getTaskById).toHaveBeenCalledWith('app', 'task-1', 'query');
expect(result).toEqual(taskDetails);
});
it('should propagate non-404 errors from the Runtime Bundle without falling back', () => {
const error = { status: 500 };
taskCloudService.getTaskById.withArgs('app', 'task-1', 'rb').and.returnValue(throwError(() => error));
let caught: unknown;
strategy.getTaskDetails$('app', 'task-1').subscribe({ error: (err) => (caught = err) });
expect(caught).toBe(error);
expect(taskCloudService.getTaskById).not.toHaveBeenCalledWith('app', 'task-1', 'query');
});
it('should delegate claim eligibility to the state-based check when permissions are absent', () => {
taskCloudService.canClaimTaskByState.and.returnValue(true);
expect(strategy.canClaim({ ...taskDetails, permissions: undefined })).toBe(true);
expect(taskCloudService.canClaimTaskByState).toHaveBeenCalled();
expect(taskCloudService.canClaimTask).not.toHaveBeenCalled();
});
it('should delegate claim eligibility to the permission-based check when permissions are present', () => {
taskCloudService.canClaimTask.and.returnValue(true);
expect(strategy.canClaim({ ...taskDetails, permissions: ['CLAIM'] })).toBe(true);
expect(taskCloudService.canClaimTask).toHaveBeenCalled();
expect(taskCloudService.canClaimTaskByState).not.toHaveBeenCalled();
});
it('should delegate unclaim eligibility to the state-based check when permissions are absent', () => {
taskCloudService.canUnclaimTaskByState.and.returnValue(true);
expect(strategy.canUnclaim({ ...taskDetails, permissions: undefined })).toBe(true);
expect(taskCloudService.canUnclaimTaskByState).toHaveBeenCalled();
expect(taskCloudService.canUnclaimTask).not.toHaveBeenCalled();
});
it('should delegate unclaim eligibility to the permission-based check when permissions are present', () => {
taskCloudService.canUnclaimTask.and.returnValue(true);
expect(strategy.canUnclaim({ ...taskDetails, permissions: ['RELEASE'] })).toBe(true);
expect(taskCloudService.canUnclaimTask).toHaveBeenCalled();
expect(taskCloudService.canUnclaimTaskByState).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,55 @@
/*!
* @license
* Copyright © 2005-2025 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 { inject, Injectable } from '@angular/core';
import { catchError, Observable, throwError } from 'rxjs';
import { TaskCloudService } from '../../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../../models/task-details-cloud.model';
import { TaskDetailsSourceStrategy } from './task-details-source.strategy';
/**
* Reads task details from the Runtime Bundle (always up to date, unlike the eventually
* consistent Query Service). Terminal tasks are no longer served by the Runtime Bundle, so a
* `404` transparently falls back to the Query Service.
*
* Claim/unclaim eligibility prefers the candidate `permissions` when the Runtime Bundle
* provides them, and falls back to a task-state evaluation when it does not (its responses
* historically omit `permissions`). This way the strategy keeps working as-is if the Runtime
* Bundle starts returning `permissions` in the future.
*/
@Injectable({ providedIn: 'root' })
export class RuntimeBundleTaskDetailsSourceStrategy implements TaskDetailsSourceStrategy {
private readonly taskCloudService = inject(TaskCloudService);
getTaskDetails$(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
return this.taskCloudService
.getTaskById(appName, taskId, 'rb')
.pipe(
catchError((error) => (error?.status === 404 ? this.taskCloudService.getTaskById(appName, taskId, 'query') : throwError(() => error)))
);
}
canClaim(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails?.permissions ? this.taskCloudService.canClaimTask(taskDetails) : this.taskCloudService.canClaimTaskByState(taskDetails);
}
canUnclaim(taskDetails: TaskDetailsCloudModel): boolean {
return taskDetails?.permissions
? this.taskCloudService.canUnclaimTask(taskDetails)
: this.taskCloudService.canUnclaimTaskByState(taskDetails);
}
}
@@ -0,0 +1,34 @@
/*!
* @license
* Copyright © 2005-2025 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 { Observable } from 'rxjs';
import { TaskDetailsCloudModel } from '../../../../models/task-details-cloud.model';
export type TaskDetailsSource = 'query' | 'rb';
/**
* Encapsulates how a task's details are read and how its claim/unclaim eligibility is
* evaluated for a given backend source, so the component can stay agnostic of the source.
*/
export interface TaskDetailsSourceStrategy {
/** Fetch the task details for the given app and task. */
getTaskDetails$(appName: string, taskId: string): Observable<TaskDetailsCloudModel>;
/** Whether the task can be claimed. */
canClaim(taskDetails: TaskDetailsCloudModel): boolean;
/** Whether the task can be unclaimed. */
canUnclaim(taskDetails: TaskDetailsCloudModel): boolean;
}
@@ -153,6 +153,105 @@ describe('UserTaskCloudComponent', () => {
});
});
describe('taskDetailsSource', () => {
beforeEach(() => {
fixture.componentRef.setInput('appName', 'app1');
});
it('should fetch the task from the Query Service by default', () => {
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1');
});
it('should fetch the task from the Runtime Bundle when taskDetailsSource is rb', () => {
fixture.componentRef.setInput('taskDetailsSource', 'rb');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1', 'rb');
});
it('should fall back to the Query Service when the Runtime Bundle returns 404 for a terminal task', () => {
getTaskSpy.withArgs('app1', 'task1', 'rb').and.returnValue(throwError(() => ({ status: 404 })));
getTaskSpy.withArgs('app1', 'task1', 'query').and.returnValue(of(taskDetails));
fixture.componentRef.setInput('taskDetailsSource', 'rb');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1', 'rb');
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1', 'query');
expect(component.taskDetails).toEqual(taskDetails);
expect(errorEmitSpy).not.toHaveBeenCalled();
});
it('should fall back to the Query Service when taskDetailsSource is an unsupported value', () => {
fixture.componentRef.setInput('taskDetailsSource', 'unsupported');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1');
expect(component.taskDetails).toEqual(taskDetails);
expect(errorEmitSpy).not.toHaveBeenCalled();
});
it('should reload the task from the new source when taskDetailsSource changes after init', () => {
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1');
getTaskSpy.calls.reset();
fixture.componentRef.setInput('taskDetailsSource', 'rb');
fixture.detectChanges();
expect(getTaskSpy).toHaveBeenCalledWith('app1', 'task1', 'rb');
});
it('should emit error when the Runtime Bundle fails with a non-404 error', () => {
const error = { status: 500 };
getTaskSpy.withArgs('app1', 'task1', 'rb').and.returnValue(throwError(() => error));
fixture.componentRef.setInput('taskDetailsSource', 'rb');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
expect(errorEmitSpy).toHaveBeenCalledWith(error);
});
describe('claim/unclaim checks in runtime bundle mode (no permissions)', () => {
beforeEach(() => {
fixture.componentRef.setInput('taskDetailsSource', 'rb');
spyOn(component, 'hasCandidateUsersOrGroups').and.returnValue(true);
});
it('should allow claiming a created, non-standalone task even without permissions', () => {
component.taskDetails = { status: TASK_CREATED_STATE, standalone: false } as TaskDetailsCloudModel;
expect(component.canClaimTask()).toBe(true);
});
it('should not allow claiming a task that is not in the created state', () => {
component.taskDetails = { status: TASK_ASSIGNED_STATE, standalone: false } as TaskDetailsCloudModel;
expect(component.canClaimTask()).toBe(false);
});
it('should allow releasing a task assigned to the current user even without permissions', () => {
getCurrentUserSpy.and.returnValue({ username: 'admin.adf' });
component.taskDetails = { status: TASK_ASSIGNED_STATE, assignee: 'admin.adf', standalone: false } as TaskDetailsCloudModel;
expect(component.canUnclaimTask()).toBe(true);
});
it('should not allow releasing a task assigned to a different user', () => {
getCurrentUserSpy.and.returnValue({ username: 'admin.adf' });
component.taskDetails = { status: TASK_ASSIGNED_STATE, assignee: 'another.user', standalone: false } as TaskDetailsCloudModel;
expect(component.canUnclaimTask()).toBe(false);
});
});
});
describe('Claim/Unclaim buttons', () => {
beforeEach(() => {
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
@@ -339,6 +438,15 @@ describe('UserTaskCloudComponent', () => {
expect(errorEmitSpy).toHaveBeenCalledWith('getTaskyById error');
});
it('should reset the loading state when getTaskById fails', async () => {
getTaskSpy.and.returnValue(throwError(() => 'getTaskyById error'));
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
await fixture.whenStable();
expect(component.loading).toBe(false);
});
});
describe('Events', () => {
@@ -33,6 +33,9 @@ import { CompleteTaskDirective } from './complete-task/complete-task.directive';
import { catchError, EMPTY, forkJoin } from 'rxjs';
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
import { UserTaskContentType, TaskTypeResolverService, UserTaskType } from '../../../../services/task-type-resolver/task-type-resolver.service';
import { TaskDetailsSource, TaskDetailsSourceStrategy } from './task-details-source/task-details-source.strategy';
import { QueryTaskDetailsSourceStrategy } from './task-details-source/query-task-details-source.strategy';
import { RuntimeBundleTaskDetailsSourceStrategy } from './task-details-source/runtime-bundle-task-details-source.strategy';
@Component({
selector: 'adf-cloud-user-task',
@@ -131,6 +134,17 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
@Input()
taskId: string;
/**
* Backend service used to fetch the task details.
*
* Defaults to `'query'` (Query Service). Set to `'rb'` to read the task from
* the Runtime Bundle, which is always up to date (the Query Service is
* eventually consistent). In `'rb'` mode, terminal tasks that are no longer
* served by the Runtime Bundle are transparently fetched from the Query Service.
*/
@Input()
taskDetailsSource: TaskDetailsSource = 'query';
/** Emitted when the cancel button is clicked. */
@Output()
cancelClick = new EventEmitter<string>();
@@ -192,6 +206,11 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
private readonly taskTypeResolverService = inject(TaskTypeResolverService);
private readonly destroyRef = inject(DestroyRef);
private readonly taskDetailsSourceStrategies: Record<TaskDetailsSource, TaskDetailsSourceStrategy> = {
query: inject(QueryTaskDetailsSourceStrategy),
rb: inject(RuntimeBundleTaskDetailsSourceStrategy)
};
ngOnChanges(changes: SimpleChanges) {
const appName = changes['appName'];
if (appName && appName.currentValue !== appName.previousValue && this.taskId) {
@@ -204,6 +223,12 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
this.loadTask();
return;
}
const taskDetailsSource = changes['taskDetailsSource'];
if (taskDetailsSource && !taskDetailsSource.firstChange && this.appName && this.taskId) {
this.loadTask();
return;
}
}
ngOnInit() {
@@ -213,7 +238,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
}
canClaimTask(): boolean {
return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups();
return !this.readOnly && this.taskDetailsStrategy.canClaim(this.taskDetails) && this.hasCandidateUsersOrGroups();
}
canCompleteTask(): boolean {
@@ -221,7 +246,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
}
canUnclaimTask(): boolean {
return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups();
return !this.readOnly && this.taskDetailsStrategy.canUnclaim(this.taskDetails) && this.hasCandidateUsersOrGroups();
}
getTaskType(): void {
@@ -299,7 +324,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
private loadTask(): void {
this.loading = true;
const tasks$ = this.taskCloudService.getTaskById(this.appName, this.taskId);
const tasks$ = this.taskDetailsStrategy.getTaskDetails$(this.appName, this.taskId);
const candidateUsers$ = this.taskCloudService.getCandidateUsers(this.appName, this.taskId);
const candidateGroups$ = this.taskCloudService.getCandidateGroups(this.appName, this.taskId);
@@ -311,6 +336,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
.pipe(
takeUntilDestroyed(this.destroyRef),
catchError((error) => {
this.loading = false;
this.onError(error);
return EMPTY;
})
@@ -325,6 +351,10 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
});
}
private get taskDetailsStrategy(): TaskDetailsSourceStrategy {
return this.taskDetailsSourceStrategies[this.taskDetailsSource] ?? this.taskDetailsSourceStrategies.query;
}
public switchToDisplayMode(newDisplayMode?: string): void {
if (this.adfCloudTaskForm) {
this.adfCloudTaskForm.switchToDisplayMode(newDisplayMode);