Skip to content

Commit

Permalink
Merge pull request #877 from TIBCOSoftware/fix-run-error-handler
Browse files Browse the repository at this point in the history
Fix run from root activity and error handler issue
  • Loading branch information
fcastill authored Sep 10, 2018
2 parents a9794b8 + 881d334 commit c1815cd
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 28 deletions.
15 changes: 8 additions & 7 deletions src/client/flogo/core/services/restapi/run-api.service.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { get, filter as _filter } from 'lodash';
import { Inject, Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

Expand Down Expand Up @@ -102,10 +103,10 @@ export class RunApiService {
// the using this.runState.processInstanceId to restart
restartFrom(processInstanceId: string, step: number, interceptor: Interceptor, updateProcessId?: string): Observable<RestartResponse> {
// get the state of the last step
const snapshotId = step - 1;
if (snapshotId < 0) {
if (step < 0) {
return _throw(new Error(`Invalid step ${step} to start from.`));
}
const snapshotId = step !== 0 ? step : 1;

return this.getSnapshot(processInstanceId, snapshotId)
.pipe(
Expand All @@ -115,7 +116,7 @@ export class RunApiService {
return this.fetch(`flows/run/flows/${updateProcessId}`)
.pipe(
map(flowInfo => {
const links = _.get(flowInfo, 'rootTask.links', []);
const links = get(flowInfo, 'rootTask.links', []);
return { snapshot, links };
})
);
Expand Down Expand Up @@ -173,12 +174,12 @@ export class RunApiService {
let taskData = snapshot.rootTaskEnv.taskDatas || [];

// filter out the tasks that are not in the path
workQueue = _.filter(workQueue, (queueItem: any) => {
workQueue = _filter(workQueue, (queueItem: any) => {
return taskIds.indexOf(queueItem.taskID) !== -1;
});

// filter out the tasks that are not in the path
taskData = _.filter(taskData, (taskDatum: any) => {
taskData = _filter(taskData, (taskDatum: any) => {
return taskDatum.taskId === '1' || taskDatum.taskId === 1 || taskIds.indexOf(taskDatum.taskId) !== -1;
});

Expand All @@ -192,7 +193,7 @@ export class RunApiService {
// TODO: left algorithm as it was when refactored, need to make it clearer
private findTaskIdsInLinkPath(tasks: InterceptorTask[], links: flowToJSON_Link[]) {
// TODO: icpt, what does it mean?? for icpTaskIds
const tasksIdsInPath: string[] = _.map(tasks, (task: any) => task.id);
const tasksIdsInPath: string[] = (tasks || []).map((task: any) => task.id);
let linksToGo = links.slice();
let lastLinksToGoLength = linksToGo.length;

Expand All @@ -209,7 +210,7 @@ export class RunApiService {

// once the linksToGo stay the same or empty, then finish
while (linksToGo.length) {
linksToGo = _.filter(linksToGo, filterLinksAndAccumulateTasks);
linksToGo = _filter(linksToGo, filterLinksAndAccumulateTasks);
if (lastLinksToGoLength === linksToGo.length) {
break;
}
Expand Down
14 changes: 13 additions & 1 deletion src/client/flogo/flow/core/state/flow/flow.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const getCurrentHandlerId = createSelector(
);

export const getCurrentGraph = createSelector(selectFlowState, getCurrentHandlerId, (flowState, currentHandlerId) => {
return flowState[getGraphName(currentHandlerId)];
return flowState[getGraphName(currentHandlerId)] as FlowGraph;
});

export const getSelectionForCurrentHandler = createSelector(
Expand Down Expand Up @@ -155,6 +155,18 @@ export const getIsRunDisabledForSelectedActivity = createSelector(
},
);

export const isCurrentSelectionRoot = createSelector(
getSelectedActivity,
getCurrentGraph,
(activity, currentGraph): boolean => activity && currentGraph && currentGraph.rootId === activity.id
);

export const getIsRestartableTask = createSelector(
getCurrentHandlerType,
isCurrentSelectionRoot,
(handlerType, isRoot) => handlerType !== HandlerType.Error && !isRoot,
);

export const getCurrentActivityExecutionErrors = createSelector(
getSelectedActivity,
getCurrentNodes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,37 @@ describe('Service: RunOrchestratorService', function (this: {

});

it('Should not send error handler if empty', () => {
spyOn(flowUtils, 'flogoFlowToJSON').and.returnValue({ id: 'transformed-flow', flow: { errorHandlerTask: { tasks: {} } } });
const storeProcessMock = <jasmine.Spy> this.runServiceMock.storeProcess;
storeProcessMock.and.returnValue(of({ id: '456' }));

const testFlow = <any> { name: 'test-flow' };
return this.service.registerFlowIfNeeded({ useFlow: <UiFlow>testFlow })
.toPromise()
.then(() => {
expect(storeProcessMock.calls.mostRecent().args[0].flow.errorHandlerTask)
.toBeUndefined('Was not expecting error handler to be present');
});
});

it('Should send error flow if not empty', () => {
spyOn(flowUtils, 'flogoFlowToJSON').and.returnValue({
id: 'transformed-flow',
flow: { errorHandlerTask: { tasks: { 'hi': { id: 'hi' } } } } }
);
const storeProcessMock = <jasmine.Spy> this.runServiceMock.storeProcess;
storeProcessMock.and.returnValue(of({ id: '456' }));

const testFlow = <any> { name: 'test-flow' };
return this.service.registerFlowIfNeeded({ useFlow: <UiFlow>testFlow })
.toPromise()
.then(() => {
expect(storeProcessMock.calls.mostRecent().args[0].flow.errorHandlerTask)
.toBeDefined('Was expecting error handler to be present but it was not present');
});
});

});

describe('::streamSteps', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
import { Injectable } from '@angular/core';
import { Observable, combineLatest, merge, timer, of, throwError as _throw } from 'rxjs';

import { isEqual, defaults } from 'lodash';
import { isEqual, isEmpty, defaults } from 'lodash';

import { Interceptor, Step, UiFlow } from '@flogo/core';
import {
Expand Down Expand Up @@ -313,6 +313,10 @@ export class RunOrchestratorService {
if (opts.useFlow) {
// generate process based on the current flow
const process = flogoFlowToJSON(opts.useFlow);
const errorHandler = process.flow && process.flow.errorHandlerTask;
if (errorHandler && isEmpty(errorHandler.tasks)) {
delete process.flow.errorHandlerTask;
}

// delete the id of the flow,
// since the same process ID returns 204 No Content response and cannot be updated,
Expand Down
23 changes: 7 additions & 16 deletions src/client/flogo/flow/core/test-runner/test-runner.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ export class TestRunnerService implements OnDestroy {
throw new Error('Cannot find proper step to restart from, skipping...');
}

const step = this.getStepNumberFromSteps(taskId);
if (!step) {
const stepNumber = this.getStepNumberFromSteps(taskId);
if (stepNumber < 0) {
// TODO
// handling the case that trying to start from the middle of a path without run from the trigger for the first time.
throw new Error(`Cannot start from task ${(<any>selectedTask).name} (${selectedTask.id})`);
Expand All @@ -112,7 +112,7 @@ export class TestRunnerService implements OnDestroy {
return this.orchestrator.rerun({
useFlow: flowState,
interceptor: dataOfInterceptor,
step: step,
step: stepNumber,
instanceId: flowState.lastFullExecution.instanceId,
});
}),
Expand All @@ -139,20 +139,11 @@ export class TestRunnerService implements OnDestroy {
// get step index logic should be based on the selected snapshot,
// hence need to be refined in the future
private getStepNumberFromSteps(taskId: string) {
let stepNumber = 0;
// firstly try to get steps from the last process instance running from the beginning,
// otherwise use some defauts
// try to get steps from the last process instance running from the beginning,
// otherwise use some defaults
const steps = get(this.runState.lastProcessInstanceFromBeginning, 'steps', this.runState.steps || []);

steps.forEach((step: any, index: number) => {
// allowing double equals for legacy ids that were of type number
/* tslint:disable-next-line:triple-equals */
if (step.taskId == taskId) {
stepNumber = index + 1;
}
});

return stepNumber;
/* tslint:disable-next-line:triple-equals - allowing double equals for legacy ids that were of type number */
return steps.findIndex(step => step.taskId == taskId);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
[executionErrors]="executionErrrors$ | async"
[activityHasRun]="activityHasRun$ | async"
[flowHasRun]="flowHasRun$ | async"
[canRestart]="isRestartableTask$ | async"
[runDisabled]="isRunDisabled$ | async"
[isEndOfFlow]="isEndOfFlow$ | async"
(runFromHere)="run()"></flogo-flow-debug-panel-content>
Expand Down
2 changes: 2 additions & 0 deletions src/client/flogo/flow/debug-panel/debug-panel.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class DebugPanelComponent implements OnInit, OnDestroy {
activityHasRun$: Observable<boolean>;
executionErrrors$: Observable<Array<string>>;
isEndOfFlow$: Observable<boolean>;
isRestartableTask$: Observable<boolean>;

private destroy$ = SingleEmissionSubject.create();

Expand All @@ -67,6 +68,7 @@ export class DebugPanelComponent implements OnInit, OnDestroy {
this.activity$ = selectAndShare(FlowSelectors.getSelectedActivity);
this.isRunDisabled$ = selectAndShare(FlowSelectors.getIsRunDisabledForSelectedActivity);
this.executionErrrors$ = selectAndShare(FlowSelectors.getCurrentActivityExecutionErrors);
this.isRestartableTask$ = selectAndShare(FlowSelectors.getIsRestartableTask);

const schema$ = selectAndShare(FlowSelectors.getSelectedActivitySchema);

Expand Down
4 changes: 2 additions & 2 deletions src/client/flogo/flow/debug-panel/error/error.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="fields-title" translate="DEBUGGER:ERROR-TITLE"></div>
<div class="form-group">
<ng-container *ngIf="!renderableError?.translate; else emptyError">
<textarea disabled class="form-control tc-text-areas tc-text-area field" value="{{ renderableError.msg | translate }}"></textarea>
<ng-container *ngIf="renderableError?.translate; else emptyError">
<textarea disabled class="form-control tc-text-areas tc-text-area field">{{ renderableError.msg | translate }}</textarea>
</ng-container>
<ng-template #emptyError>
<textarea disabled class="form-control tc-text-areas tc-text-area field" [value]="renderableError.msg"></textarea>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<div class="activity__name">{{ activity.name }}</div>
<div class="activity__id">ID: {{ activity.id }}</div>
<div class="activity__description">{{ activity.description }}</div>
<button class="flogo-button--secondary btn-run" [disabled]="runDisabled" (click)="onRun()">{{ 'DEBUGGER:BUTTON-RUN-FROM-TILE' | translate }}</button>
<button *ngIf="canRestart" class="flogo-button--secondary btn-run"
[disabled]="runDisabled" (click)="onRun()">{{ 'DEBUGGER:BUTTON-RUN-FROM-TILE' | translate }}</button>
</div>
<ng-container *ngIf="executionErrors?.length > 0; then errorDisplay else activityFields"></ng-container>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export class PanelContentComponent implements OnChanges {
@Input() activity: ItemActivityTask;
@Input() fields: FieldsInfo;
@Input() executionErrors?: Array<string>;
@Input() canRestart: boolean;
@Input() runDisabled: boolean;
@Input() isEndOfFlow: boolean;
@Input() activityHasRun: boolean;
Expand Down

0 comments on commit c1815cd

Please sign in to comment.