This is an automated email from the ASF dual-hosted git repository. riemer pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git
commit 93b7df1703fa94923b22075d17ab6c4e5ea423e1 Author: Dominik Riemer <[email protected]> AuthorDate: Thu Sep 17 23:20:52 2020 +0200 [hotfix] Fix bug that caused pipeline element recommender to crash --- .../manager/matching/ConnectionValidator.java | 4 +- .../manager/matching/InvocationGraphBuilder.java | 3 +- .../matching/PipelineVerificationHandler.java | 18 +++-- .../manager/recommender/ElementRecommender.java | 20 +++-- .../pipeline-element-options.component.html | 91 +++++++++++----------- .../pipeline-element-options.component.ts | 21 +++-- .../pipeline-element-recommendation.component.html | 4 +- .../pipeline-element-recommendation.component.ts | 35 ++++----- .../components/pipeline/pipeline.component.ts | 3 +- 9 files changed, 106 insertions(+), 93 deletions(-) diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/ConnectionValidator.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/ConnectionValidator.java index a52126a..4e383c5 100644 --- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/ConnectionValidator.java +++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/ConnectionValidator.java @@ -44,7 +44,7 @@ public class ConnectionValidator { this.verifier = new ElementVerification(); } - public void validateConnection() throws InvalidConnectionException { + public List<InvocableStreamPipesEntity> validateConnection() throws InvalidConnectionException { boolean verified = true; InvocableStreamPipesEntity rightElement = rootPipelineElement; List<String> connectedTo = rootPipelineElement.getConnectedTo(); @@ -66,6 +66,8 @@ public class ConnectionValidator { if (!verified) { throw new InvalidConnectionException(verifier.getErrorLog()); } + + return invocationGraphs; } private DataProcessorInvocation findInvocationGraph(List<InvocableStreamPipesEntity> graphs, String domId) { diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/InvocationGraphBuilder.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/InvocationGraphBuilder.java index fc2547e..7a2ffc8 100644 --- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/InvocationGraphBuilder.java +++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/InvocationGraphBuilder.java @@ -72,7 +72,7 @@ public class InvocationGraphBuilder { .getEventGrounding(); if (source instanceof InvocableStreamPipesEntity) { - if (source instanceof DataProcessorInvocation) { + if (source instanceof DataProcessorInvocation && ((DataProcessorInvocation) source).isConfigured()) { DataProcessorInvocation dataProcessorInvocation = (DataProcessorInvocation) source; Tuple2<EventSchema, ? extends OutputStrategy> outputSettings; @@ -190,4 +190,5 @@ public class InvocationGraphBuilder { .findFirst() .get(); } + } \ No newline at end of file diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java index bbdc6bd..ff80bdb 100644 --- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java +++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/matching/PipelineVerificationHandler.java @@ -44,7 +44,7 @@ public class PipelineVerificationHandler { private final Pipeline pipeline; private final PipelineModificationMessage pipelineModificationMessage; - private final List<InvocableStreamPipesEntity> invocationGraphs; + private List<InvocableStreamPipesEntity> invocationGraphs; private final InvocableStreamPipesEntity rootPipelineElement; public PipelineVerificationHandler(Pipeline pipeline) throws NoSepaInPipelineException { @@ -61,7 +61,8 @@ public class PipelineVerificationHandler { * @throws InvalidConnectionException if the connection is not considered valid */ public PipelineVerificationHandler validateConnection() throws InvalidConnectionException { - new ConnectionValidator(pipeline, invocationGraphs, rootPipelineElement).validateConnection(); + invocationGraphs = new ConnectionValidator(pipeline, invocationGraphs, rootPipelineElement) + .validateConnection(); return this; } @@ -182,13 +183,13 @@ public class PipelineVerificationHandler { return pipelineModificationMessage; } + public List<InvocableStreamPipesEntity> getInvocationGraphs() { + return this.invocationGraphs; + } + public List<InvocableStreamPipesEntity> makeInvocationGraphs() { - if (onlyStreamAncestorsPresentInPipeline()) { - return new ArrayList<>(); - } else { - PipelineGraph pipelineGraph = new PipelineGraphBuilder(pipeline).buildGraph(); - return new InvocationGraphBuilder(pipelineGraph, null).buildGraphs(); - } + PipelineGraph pipelineGraph = new PipelineGraphBuilder(pipeline).buildGraph(); + return new InvocationGraphBuilder(pipelineGraph, null).buildGraphs(); } private boolean onlyStreamAncestorsPresentInPipeline() { @@ -198,4 +199,5 @@ public class PipelineVerificationHandler { .map(connectedTo -> TreeUtils.findSEPAElement(connectedTo, pipeline.getSepas(), pipeline.getStreams())) .allMatch(pe -> pe instanceof SpDataStream); } + } \ No newline at end of file diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/recommender/ElementRecommender.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/recommender/ElementRecommender.java index d409d77..60f2b34 100644 --- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/recommender/ElementRecommender.java +++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/recommender/ElementRecommender.java @@ -20,7 +20,9 @@ package org.apache.streampipes.manager.recommender; import org.apache.streampipes.commons.exceptions.NoSepaInPipelineException; import org.apache.streampipes.commons.exceptions.NoSuitableSepasAvailableException; -import org.apache.streampipes.manager.matching.PipelineVerificationHandler; +import org.apache.streampipes.manager.data.PipelineGraph; +import org.apache.streampipes.manager.data.PipelineGraphBuilder; +import org.apache.streampipes.manager.matching.InvocationGraphBuilder; import org.apache.streampipes.manager.matching.v2.StreamMatch; import org.apache.streampipes.manager.storage.UserManagementService; import org.apache.streampipes.manager.util.PipelineVerificationUtils; @@ -30,17 +32,19 @@ import org.apache.streampipes.model.base.InvocableStreamPipesEntity; import org.apache.streampipes.model.base.NamedStreamPipesEntity; import org.apache.streampipes.model.client.exception.InvalidConnectionException; import org.apache.streampipes.model.client.matching.MatchingResultMessage; -import org.apache.streampipes.model.pipeline.Pipeline; -import org.apache.streampipes.model.pipeline.PipelineElementRecommendation; -import org.apache.streampipes.model.pipeline.PipelineElementRecommendationMessage; import org.apache.streampipes.model.graph.DataProcessorDescription; import org.apache.streampipes.model.graph.DataProcessorInvocation; import org.apache.streampipes.model.graph.DataSinkDescription; import org.apache.streampipes.model.graph.DataSinkInvocation; +import org.apache.streampipes.model.pipeline.Pipeline; +import org.apache.streampipes.model.pipeline.PipelineElementRecommendation; +import org.apache.streampipes.model.pipeline.PipelineElementRecommendationMessage; import org.apache.streampipes.storage.api.INoSqlStorage; import org.apache.streampipes.storage.api.IPipelineElementDescriptionStorage; import org.apache.streampipes.storage.management.StorageDispatcher; import org.apache.streampipes.storage.management.StorageManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -49,6 +53,8 @@ import java.util.stream.Collectors; public class ElementRecommender { + private static final Logger LOG = LoggerFactory.getLogger(ElementRecommender.class); + private Pipeline pipeline; private String email; private PipelineElementRecommendationMessage recommendationMessage; @@ -205,9 +211,9 @@ public class ElementRecommender { } else if (rootNode instanceof DataSinkInvocation) { return Optional.empty(); } else { - List<InvocableStreamPipesEntity> graphs = new PipelineVerificationHandler(pipeline) - .validateConnection() - .makeInvocationGraphs(); + ((DataProcessorInvocation) rootNode).setConfigured(true); + PipelineGraph pipelineGraph = new PipelineGraphBuilder(pipeline).buildGraph(); + List<InvocableStreamPipesEntity> graphs = new InvocationGraphBuilder(pipelineGraph, null).buildGraphs(); Optional<InvocableStreamPipesEntity> rootElementWithOutputStream = graphs .stream() diff --git a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html index b345465..976a382 100644 --- a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html +++ b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.html @@ -15,55 +15,56 @@ ~ limitations under the License. ~ --> - -<div *ngIf="currentMouseOverElement==pipelineElement.payload.dom" class="sp-fade-options"> - <span class="options-button customize-button" *ngIf="pipelineElement.type!='stream'" style="z-index:10"> - <button mat-button mat-icon-button matTooltip="Configure Element" - [matTooltipPosition]="'above'" - (click)="customizeElement(pipelineElement)"> - <i class="material-icons">settings</i> +<div> + <div [ngStyle]="currentMouseOverElement==pipelineElement.payload.dom ? {opacity: 1} : {opacity: 0}" class="sp-fade-options"> + <span class="options-button customize-button" *ngIf="pipelineElement.type!='stream'" style="z-index:10"> + <button mat-button mat-icon-button matTooltip="Configure Element" + [matTooltipPosition]="'above'" + (click)="customizeElement(pipelineElement)"> + <i class="material-icons">settings</i> + </button> + </span> + <span class="options-button customize-button" + *ngIf="pipelineElement.type=='stream' && isWildcardTopic()" + style="z-index:10"> + <button mat-button mat-icon-button matTooltip="Configure Element" [matTooltipPosition]="'above'" + (click)="openCustomizeStreamDialog()" [disabled]="!isRootElement()"> + <i class="material-icons">settings</i> </button> - </span> - <span class="options-button customize-button" - *ngIf="pipelineElement.type=='stream' && isWildcardTopic()" - style="z-index:10"> - <button mat-button mat-icon-button matTooltip="Configure Element" [matTooltipPosition]="'above'" - (click)="openCustomizeStreamDialog()" [disabled]="!isRootElement()"> - <i class="material-icons">settings</i> - </button> - </span> - <span class="options-button delete-button" style="z-index:10"> - <button mat-button mat-icon-button matTooltip="Delete Element" [matTooltipPosition]="'above'" - (click)="removeElement(pipelineElement)" [disabled]="!isRootElement()"> - <i class="material-icons">clear</i> - </button> - </span> - <span class="options-button possible-button" *ngIf="pipelineElement.type!='action'" style="z-index:10"> - <button mat-button mat-icon-button matTooltip="Compatible Elements" [matTooltipPosition]="'below'" - [disabled]="!possibleElements || possibleElements.length == 0 || !isConfigured()" - (click)="openPossibleElementsDialog()"> - <i class="material-icons">visibility</i> - </button> - </span> - <span class="options-button recommended-button" - *ngIf="pipelineElement.type!='action' && (recommendationsAvailable) && recommendedElements.length > 0" - style="z-index:10"> - <button mat-button mat-icon-button matTooltip="Recommended Elements" [matTooltipPosition]="'below'" - (click)="showRecommendations($event)" - [disabled]="!recommendationsAvailable || !isConfigured()"> - <i class="material-icons">add</i> + </span> + <span class="options-button delete-button" style="z-index:10"> + <button mat-button mat-icon-button matTooltip="Delete Element" [matTooltipPosition]="'above'" + (click)="removeElement(pipelineElement)" [disabled]="!isRootElement()"> + <i class="material-icons">clear</i> + </button> + </span> + <span class="options-button possible-button" *ngIf="pipelineElement.type!='action'" style="z-index:10"> + <button mat-button mat-icon-button matTooltip="Compatible Elements" [matTooltipPosition]="'below'" + [disabled]="!possibleElements || possibleElements.length == 0 || !isConfigured()" + (click)="openPossibleElementsDialog()"> + <i class="material-icons">visibility</i> </button> - </span> - <span class="options-button help-button" style="z-index:10"> - <button matTooltip="Help" [matTooltipPosition]="'below'" - mat-button mat-icon-button (click)="openHelpDialog()">? + </span> + <span class="options-button recommended-button" + *ngIf="pipelineElement.type!='action' && (recommendationsAvailable) && recommendedElements.length > 0" + style="z-index:10"> + <button mat-button mat-icon-button matTooltip="Recommended Elements" [matTooltipPosition]="'below'" + (click)="showRecommendations($event)" + [disabled]="!recommendationsAvailable || !isConfigured()"> + <i class="material-icons">add</i> </button> - </span> - <div class="editor-pe-info" [ngClass]="'pe-info-' + pipelineElementCssType"> - {{pipelineElement.payload.name}} + </span> + <span class="options-button help-button" style="z-index:10"> + <button matTooltip="Help" [matTooltipPosition]="'below'" + mat-button mat-icon-button (click)="openHelpDialog()">? + </button> + </span> + <div class="editor-pe-info" [ngClass]="'pe-info-' + pipelineElementCssType"> + {{pipelineElement.payload.name}} + </div> </div> -</div> -<pipeline-element-recommendation [rawPipelineModel]="rawPipelineModel" + <pipeline-element-recommendation [rawPipelineModel]="rawPipelineModel" [pipelineElementDomId]="pipelineElement.payload.dom" [recommendedElements]="recommendedElements" [recommendationsShown]="recommendationsShown" *ngIf="recommendationsAvailable"></pipeline-element-recommendation> +</div> \ No newline at end of file diff --git a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts index 6a84660..6d20d18 100644 --- a/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts +++ b/ui/src/app/editor/components/pipeline-element-options/pipeline-element-options.component.ts @@ -20,7 +20,7 @@ import {JsplumbBridge} from "../../services/jsplumb-bridge.service"; import {JsplumbService} from "../../services/jsplumb.service"; import {PipelineValidationService} from "../../services/pipeline-validation.service"; import {RestApi} from "../../../services/rest-api.service"; -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; +import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from "@angular/core"; import {PipelineElementRecommendationService} from "../../services/pipeline-element-recommendation.service"; import {ObjectProvider} from "../../services/object-provider.service"; import { @@ -35,18 +35,19 @@ import {DialogService} from "../../../core-ui/dialog/base-dialog/base-dialog.ser import {CompatibleElementsComponent} from "../../dialog/compatible-elements/compatible-elements.component"; import {Tuple2} from "../../../core-model/base/Tuple2"; import { cloneDeep } from "lodash"; +import {Observable, Subscription} from "rxjs"; @Component({ selector: 'pipeline-element-options', templateUrl: './pipeline-element-options.component.html', styleUrls: ['./pipeline-element-options.component.css'] }) -export class PipelineElementOptionsComponent implements OnInit{ +export class PipelineElementOptionsComponent implements OnInit, OnDestroy { - recommendationsAvailable: any; + recommendationsAvailable: any = false; possibleElements: PipelineElementUnion[]; recommendedElements: PipelineElementUnion[]; - recommendationsShown: any; + recommendationsShown: any = false; pipelineElementCssType: string; @Input() @@ -76,6 +77,8 @@ export class PipelineElementOptionsComponent implements OnInit{ @Output() customize: EventEmitter<Tuple2<Boolean, PipelineElementConfig>> = new EventEmitter<Tuple2<Boolean, PipelineElementConfig>>(); + pipelineElementConfiguredObservable: Subscription; + constructor(private ObjectProvider: ObjectProvider, private PipelineElementRecommendationService: PipelineElementRecommendationService, private DialogService: DialogService, @@ -91,7 +94,7 @@ export class PipelineElementOptionsComponent implements OnInit{ } ngOnInit() { - this.EditorService.pipelineElementConfigured$.subscribe(pipelineElementDomId => { + this.pipelineElementConfiguredObservable = this.EditorService.pipelineElementConfigured$.subscribe(pipelineElementDomId => { this.pipelineElement.settings.openCustomize = false; this.RestApi.updateCachedPipeline(this.rawPipelineModel); if (pipelineElementDomId === this.pipelineElement.payload.dom) { @@ -134,8 +137,8 @@ export class PipelineElementOptionsComponent implements OnInit{ var currentPipeline = this.ObjectProvider.makePipeline(clonedModel); this.EditorService.recommendPipelineElement(currentPipeline).subscribe((result) => { if (result.success) { - this.possibleElements = this.PipelineElementRecommendationService.collectPossibleElements(this.allElements, result.possibleElements); - this.recommendedElements = this.PipelineElementRecommendationService.populateRecommendedList(this.allElements, result.recommendedElements); + this.possibleElements = cloneDeep(this.PipelineElementRecommendationService.collectPossibleElements(this.allElements, result.possibleElements)); + this.recommendedElements = cloneDeep(this.PipelineElementRecommendationService.populateRecommendedList(this.allElements, result.recommendedElements)); this.recommendationsAvailable = true; } }); @@ -180,4 +183,8 @@ export class PipelineElementOptionsComponent implements OnInit{ .transportProtocols[0] .topicDefinition instanceof WildcardTopicDefinition; } + + ngOnDestroy(): void { + this.pipelineElementConfiguredObservable.unsubscribe(); + } } \ No newline at end of file diff --git a/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.html b/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.html index fe13f3c..09748fc 100644 --- a/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.html +++ b/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.html @@ -22,8 +22,8 @@ [style]="recommendedElement.layoutSettings.skewStyle"> <a [style]="recommendedElement.layoutSettings.unskewStyle" [ngClass]="recommendedElement.layoutSettings.type" (click)="create(recommendedElement)" *ngIf="recommendedElement.name"> </a> - <div (click)="create(recommendedElement)" [style]="recommendedElement.layoutSettings.unskewStyleLabel | safeCss" - class="{{recommendedElement.layoutSettings.type}}"> + <div (click)="create(recommendedElement)" [style]="recommendedElement.layoutSettings.unskewStyleLabel" + [ngClass]="recommendedElement.layoutSettings.type"> <pipeline-element [iconSize]="'small'" [pipelineElement]="recommendedElement" *ngIf="recommendedElement.name"> </pipeline-element> </div> diff --git a/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.ts b/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.ts index 2d6cf8a..61ca035 100644 --- a/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.ts +++ b/ui/src/app/editor/components/pipeline-element-recommendation/pipeline-element-recommendation.component.ts @@ -17,7 +17,7 @@ */ import {JsplumbService} from "../../services/jsplumb.service"; -import {Component, Input, OnInit} from "@angular/core"; +import {AfterViewInit, ChangeDetectorRef, Component, Input, OnInit} from "@angular/core"; import {PipelineElementConfig} from "../../model/editor.model"; import {DataProcessorInvocation} from "../../../core-model/gen/streampipes-model"; import {SafeCss} from "../../utils/style-sanitizer"; @@ -27,7 +27,7 @@ import {SafeCss} from "../../utils/style-sanitizer"; templateUrl: './pipeline-element-recommendation.component.html', styleUrls: ['./pipeline-element-recommendation.component.scss'] }) -export class PipelineElementRecommendationComponent implements OnInit { +export class PipelineElementRecommendationComponent implements OnInit, AfterViewInit { @Input() recommendationsShown: boolean; @@ -38,7 +38,8 @@ export class PipelineElementRecommendationComponent implements OnInit { @Input() pipelineElementDomId: string; - _recommendedElements: any; + @Input() + recommendedElements: any; recommendationsPrepared: boolean = false; @@ -48,11 +49,16 @@ export class PipelineElementRecommendationComponent implements OnInit { } ngOnInit() { + this.fillRemainingItems(); + this.prepareStyles(this.recommendedElements); + this.recommendationsPrepared = true; + } + + ngAfterViewInit(): void { } prepareStyles(recommendedElements) { - this.fillRemainingItems(recommendedElements); recommendedElements.forEach((element, index) => { this.setLayoutSettings(element, index, recommendedElements); }); @@ -130,25 +136,12 @@ export class PipelineElementRecommendationComponent implements OnInit { return (360 / recommendedElements.length); } - fillRemainingItems(recommendedElements) { - if (recommendedElements.length < 6) { - for (var i = recommendedElements.length; i < 6; i++) { + fillRemainingItems() { + if (this.recommendedElements.length < 6) { + for (var i = this.recommendedElements.length; i < 6; i++) { let element = {fakeElement: true, weight: 0}; - //this.setLayoutSettings(element, i); - recommendedElements.push(element); + this.recommendedElements.push(element); } } } - - get recommendedElements() { - return this._recommendedElements; - } - - @Input() - set recommendedElements(recommendedElements: any) { - this.recommendationsPrepared = false; - this.prepareStyles(recommendedElements); - this._recommendedElements = recommendedElements; - this.recommendationsPrepared = true; - } } \ No newline at end of file diff --git a/ui/src/app/editor/components/pipeline/pipeline.component.ts b/ui/src/app/editor/components/pipeline/pipeline.component.ts index 6c3d013..dd83082 100644 --- a/ui/src/app/editor/components/pipeline/pipeline.component.ts +++ b/ui/src/app/editor/components/pipeline/pipeline.component.ts @@ -312,9 +312,9 @@ export class PipelineComponent implements OnInit { if ((payload.staticProperties && payload.staticProperties.length > 0) || this.isCustomOutput(pe)) { this.showCustomizeDialog({a: false, b: pe}); } else { - this.announceConfiguredElement(pe); (pe.payload as InvocablePipelineElementUnion).configured = true; pe.settings.completed = true; + this.announceConfiguredElement(pe); } } }, status => { @@ -410,6 +410,7 @@ export class PipelineComponent implements OnInit { dialogRef.afterClosed().subscribe(c => { if (c) { pipelineElementInfo.b.settings.openCustomize = false; + (pipelineElementInfo.b.payload as InvocablePipelineElementUnion).configured = true; this.JsplumbService.activateEndpoint(pipelineElementInfo.b.payload.dom, pipelineElementInfo.b.settings.completed); this.JsplumbBridge.getSourceEndpoint(pipelineElementInfo.b.payload.dom).setType("token"); this.triggerPipelineCacheUpdate();
