This is an automated email from the ASF dual-hosted git repository.
dominikriemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 11b7168d1e fix: Consistent Asset Create Workflow (#4644)
11b7168d1e is described below
commit 11b7168d1e7c9acfda3e231c4f0605b2aaf3fab5
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Sat Jun 27 22:25:54 2026 +0200
fix: Consistent Asset Create Workflow (#4644)
---
ui/cypress/support/utils/asset/AssetBtns.ts | 3 +
ui/cypress/support/utils/asset/AssetUtils.ts | 4 +-
ui/deployment/i18n/de.json | 3 +-
ui/deployment/i18n/en.json | 3 +-
ui/deployment/i18n/pl.json | 3 +-
.../asset-details/base-asset-details.directive.ts | 29 ++++-
.../edit-asset/asset-details.component.html | 8 +-
.../edit-asset/asset-details.component.ts | 120 +++++++++++++++------
.../asset-overview/asset-overview.component.ts | 21 +---
.../create-asset-dialog.component.html | 69 ------------
.../create-asset/create-asset-dialog.component.ts | 72 -------------
11 files changed, 134 insertions(+), 201 deletions(-)
diff --git a/ui/cypress/support/utils/asset/AssetBtns.ts
b/ui/cypress/support/utils/asset/AssetBtns.ts
index fc937cc33b..ff867b497b 100644
--- a/ui/cypress/support/utils/asset/AssetBtns.ts
+++ b/ui/cypress/support/utils/asset/AssetBtns.ts
@@ -22,6 +22,9 @@ export class AssetBtns {
public static createAssetBtn() {
return cy.dataCy('create-new-asset-button', { timeout: 10000 });
}
+ public static createBtn() {
+ return cy.dataCy('sp-manage-save', { timeout: 10000 });
+ }
public static assetNameInput() {
return cy.dataCy('asset-name', { timeout: 10000 });
diff --git a/ui/cypress/support/utils/asset/AssetUtils.ts
b/ui/cypress/support/utils/asset/AssetUtils.ts
index 2b6918a6ff..c2f98b9319 100644
--- a/ui/cypress/support/utils/asset/AssetUtils.ts
+++ b/ui/cypress/support/utils/asset/AssetUtils.ts
@@ -89,13 +89,14 @@ export class AssetUtils {
AssetUtils.addNewAsset(asset);
AssetBtns.saveAssetBtn().click();
+ AssetBtns.createBtn().click();
+
AssetBtns.createAssetBtn().should('be.visible');
}
public static addNewAsset(asset: Asset) {
AssetBtns.createAssetBtn().click();
AssetBtns.assetNameInput().clear().type(asset.name);
- AssetBtns.createAssetPanelBtn().click();
this.selectAssetType(asset.assetType);
if (asset.site) {
@@ -265,6 +266,7 @@ export class AssetUtils {
AssetUtils.checkAmountOfLinkedResources(2);
AssetBtns.saveAssetBtn().click();
+ AssetBtns.createBtn().click();
cy.location('hash', { timeout: 10000 }).should(
'include',
'/assets/overview',
diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index fcb8e4df5d..d6ebc2dc3b 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -115,7 +115,6 @@
"Areas": "Bereiche",
"Asset": "Asset",
"Asset Context": "Asset-Kontext",
- "Asset Name": "Asset Name",
"Asset Type": "Asset-Typ",
"Asset links": "Asset-Links",
"Assets": "Assets",
@@ -241,7 +240,6 @@
"Create a new asset in the asset view before adding it to a resource.":
"Erstellen Sie zuerst ein neues Asset in der Asset-Ansicht, bevor Sie es zu
einer Ressource hinzufügen.",
"Create a new group in the Groups section of the security page.": "Erstellen
Sie eine neue Gruppe im Abschnitt Gruppen auf der Sicherheitsseite.",
"Create adapter": "Adapter erstellen",
- "Create asset": "Asset erstellen",
"Create asset links": "Asset-Links erstellen",
"Create chart": "Diagramm erstellen",
"Create chart in new tab": "Diagramm in neuem Tab erstellen",
@@ -704,6 +702,7 @@
"New": "Neu",
"New API key": "Neuer API-Schlüssel",
"New Alternate ID": "Neue alternative ID",
+ "New Asset": "Neues Asset",
"New Export Provider": "Neuer Exportanbieter",
"New Pipeline": "Neue Pipeline",
"New adapter": "Neuer Adapter",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index 7b36992eac..bb65b520b2 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -115,7 +115,6 @@
"Areas": null,
"Asset": null,
"Asset Context": null,
- "Asset Name": null,
"Asset Type": null,
"Asset links": null,
"Assets": null,
@@ -241,7 +240,6 @@
"Create a new asset in the asset view before adding it to a resource.": null,
"Create a new group in the Groups section of the security page.": null,
"Create adapter": null,
- "Create asset": null,
"Create asset links": null,
"Create chart": null,
"Create chart in new tab": null,
@@ -704,6 +702,7 @@
"New": null,
"New API key": null,
"New Alternate ID": null,
+ "New Asset": null,
"New Export Provider": null,
"New Pipeline": null,
"New adapter": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index c04a0d71b1..cf392018df 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -115,7 +115,6 @@
"Areas": "Obszary",
"Asset": "Zasób",
"Asset Context": "Kontekst zasobu",
- "Asset Name": "Nazwa zasobu",
"Asset Type": "Typ zasobu",
"Asset links": "Linki zasobu",
"Assets": "Zasoby",
@@ -241,7 +240,6 @@
"Create a new asset in the asset view before adding it to a resource.":
"Utwórz nowy zasób w widoku zasobów, zanim dodasz go do zasobu systemowego.",
"Create a new group in the Groups section of the security page.": "Utwórz
nową grupę w sekcji Grupy na stronie bezpieczeństwa.",
"Create adapter": "Utwórz adapter",
- "Create asset": "Utwórz zasób",
"Create asset links": "Utwórz linki zasobu",
"Create chart": "Utwórz wykres",
"Create chart in new tab": "Utwórz wykres w nowej karcie",
@@ -704,6 +702,7 @@
"New": "Nowy",
"New API key": "Nowy klucz API",
"New Alternate ID": "Nowe alternatywne ID",
+ "New Asset": "Nowy składnik aktywów",
"New Export Provider": "Nowy dostawca eksportu",
"New Pipeline": "Nowy strumień",
"New adapter": "Nowy adapter",
diff --git
a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
index 2e45bc314d..c2e88960ca 100644
--- a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
+++ b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
@@ -27,7 +27,7 @@ import {
SpAssetModel,
} from '@streampipes/platform-services';
import { ActivatedRoute } from '@angular/router';
-import { zip } from 'rxjs';
+import { of, zip } from 'rxjs';
import { SpAssetRoutes } from '../../assets.breadcrumb';
@Directive()
@@ -39,6 +39,7 @@ export abstract class BaseAssetDetailsDirective implements
OnInit {
rootNode = true;
assetModelId: string;
+ protected isNewAsset = false;
private breadcrumbService = inject(SpBreadcrumbService);
protected route = inject(ActivatedRoute);
@@ -60,7 +61,10 @@ export abstract class BaseAssetDetailsDirective implements
OnInit {
}
loadResources(): void {
- const assetReq = this.assetService.getAsset(this.assetModelId);
+ const newAsset = this.getNewAssetFromNavigationState();
+ const assetReq = newAsset
+ ? of(newAsset)
+ : this.assetService.getAsset(this.assetModelId);
const locationsReq = this.genericStorageService.getAllDocuments(
AssetConstants.ASSET_SITES_APP_DOC_NAME,
);
@@ -82,6 +86,12 @@ export abstract class BaseAssetDetailsDirective implements
OnInit {
}
applySites(): void {
+ this.asset.assetSite ??= {
+ area: undefined,
+ siteId: undefined,
+ hasExactLocation: false,
+ location: undefined,
+ };
if (!this.asset.assetSite.hasExactLocation) {
const matchingSite = this.sites.find(
site => site._id === this.asset.assetSite.siteId,
@@ -98,4 +108,19 @@ export abstract class BaseAssetDetailsDirective implements
OnInit {
}
abstract onAssetAvailable(): void;
+
+ private getNewAssetFromNavigationState(): SpAssetModel | undefined {
+ const state = history.state as {
+ assetModel?: SpAssetModel;
+ isNewAsset?: boolean;
+ };
+ if (
+ state?.isNewAsset &&
+ state.assetModel?.elementId === this.assetModelId
+ ) {
+ this.isNewAsset = true;
+ return state.assetModel;
+ }
+ return undefined;
+ }
}
diff --git
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
index 3edca7e368..1dbc66d3f4 100644
---
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
+++
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
@@ -45,7 +45,13 @@
(click)="saveAsset()"
>
<i class="material-icons">save</i
- ><span> {{ 'Save' | translate }}</span>
+ ><span
+ > {{
+ isNewAsset
+ ? ('Create' | translate)
+ : ('Save' | translate)
+ }}</span
+ >
</button>
</div>
</div>
diff --git
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
index 2a4276160e..3abea23a87 100644
---
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
+++
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
@@ -51,7 +51,7 @@ import {
} from '@streampipes/platform-services';
import { MatDialog } from '@angular/material/dialog';
import { firstValueFrom, from, Observable, of } from 'rxjs';
-import { map, switchMap } from 'rxjs/operators';
+import { map, switchMap, tap } from 'rxjs/operators';
import { SupportsUnsavedChangeDialog } from
'../../../../chart-shared/models/dataview-dashboard.model';
type ManageableAsset = SpAssetModel & {
@@ -93,6 +93,10 @@ export class SpAssetDetailsComponent
private originalAsset: SpAssetModel;
async saveAsset() {
+ if (this.isNewAsset && this.pendingManageAssetResult === undefined) {
+ this.openManageAssetDialog(true);
+ return;
+ }
await this.saveAssetChanges();
this.assetBrowserService.refreshBrowserAssetData();
this.router.navigate(['assets'], {
@@ -110,41 +114,17 @@ export class SpAssetDetailsComponent
}
manageAsset(): void {
- const resource = this.makeManageableAsset(this.asset);
- const resourceConfig:
ObjectManageDialogResourceConfig<ManageableAsset> =
- {
- resourceLabel: 'Asset',
- nameLabel: 'Asset name',
- descriptionLabel: 'Asset description',
- nameProperty: 'name',
- showAssetLinking: false,
- };
- const dialogRef = this.dialogService.open(ObjectManageDialogComponent,
{
- panelType: PanelType.SLIDE_IN_PANEL,
- title: this.translateService.instant('Manage'),
- width: '50vw',
- data: {
- objectInstanceId: resource.elementId,
- resource,
- saveMode: 'deferred',
- resourceConfig,
- headerTitle:
- this.translateService.instant('Manage Asset ') +
- resource.name,
- },
- });
- dialogRef.afterClosed().subscribe(result => {
- if (result && typeof result !== 'boolean') {
- this.pendingManageAssetResult = result;
- Object.assign(
- this.asset,
- this.makeAssetResource(result.resource),
- );
- }
- });
+ this.openManageAssetDialog();
}
deleteAsset(): void {
+ if (this.isNewAsset) {
+ this.router.navigate(['assets'], {
+ state: { omitConfirm: true },
+ });
+ return;
+ }
+
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
width: '500px',
data: {
@@ -172,6 +152,73 @@ export class SpAssetDetailsComponent
});
}
+ private openManageAssetDialog(saveAfterClose = false): void {
+ const resource = this.makeManageableAsset(this.asset);
+ const resourceConfig:
ObjectManageDialogResourceConfig<ManageableAsset> =
+ {
+ resourceLabel: 'Asset',
+ nameLabel: 'Asset name',
+ descriptionLabel: 'Asset description',
+ nameProperty: 'name',
+ showAssetLinking: false,
+ saveResource: this.isNewAsset
+ ? resource =>
+ this.assetService
+ .createAsset(this.makeAssetResource(resource))
+ .pipe(
+ tap(savedAsset => {
+ Object.assign(
+ this.asset,
+ savedAsset ??
+ this.makeAssetResource(resource),
+ );
+ this.isNewAsset = false;
+ }),
+ )
+ : undefined,
+ };
+ const dialogRef = this.dialogService.open(ObjectManageDialogComponent,
{
+ panelType: PanelType.SLIDE_IN_PANEL,
+ title: this.isNewAsset
+ ? this.translateService.instant('New Asset')
+ : this.translateService.instant('Manage'),
+ width: '50vw',
+
+ data: {
+ objectInstanceId: resource.elementId,
+ resource,
+ saveMode: this.isNewAsset ? 'immediate' : 'deferred',
+ createMode: this.isNewAsset,
+ resourceConfig,
+ headerTitle: this.isNewAsset
+ ? this.translateService.instant('New Asset')
+ : this.translateService.instant('Manage Asset ') +
+ (resource.name ?? ''),
+ },
+ });
+ dialogRef.afterClosed().subscribe(result => {
+ if (saveAfterClose && result === true) {
+ this.assetBrowserService.refreshBrowserAssetData();
+ this.router.navigate(['assets'], {
+ state: { omitConfirm: true },
+ });
+ return;
+ }
+
+ if (result && typeof result !== 'boolean') {
+ this.pendingManageAssetResult = result;
+ Object.assign(
+ this.asset,
+ this.makeAssetResource(result.resource),
+ );
+
+ if (saveAfterClose) {
+ void this.saveAsset();
+ }
+ }
+ });
+ }
+
confirmLeaveDialog(
_route: ActivatedRouteSnapshot,
_state: RouterStateSnapshot,
@@ -240,7 +287,12 @@ export class SpAssetDetailsComponent
private async saveAssetChanges(): Promise<void> {
this.cleanupEmpty();
- await firstValueFrom(this.assetService.updateAsset(this.asset));
+ if (this.isNewAsset) {
+ await firstValueFrom(this.assetService.createAsset(this.asset));
+ this.isNewAsset = false;
+ } else {
+ await firstValueFrom(this.assetService.updateAsset(this.asset));
+ }
await this.savePendingManageAssetChanges();
this.originalAsset = this.cloneAsset(this.asset);
}
diff --git
a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
index 0dab1654a3..92a403fc1c 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
@@ -46,7 +46,6 @@ import {
} from '@streampipes/shared-ui';
import { SpAssetRoutes } from '../../assets.breadcrumb';
import { Router } from '@angular/router';
-import { SpCreateAssetDialogComponent } from
'../../dialog/create-asset/create-asset-dialog.component';
import { IdGeneratorService } from
'../../../core-services/id-generator/id-generator.service';
import { UserPrivilege } from '../../../core/auth/user-privilege.enum';
import { MatDialog } from '@angular/material/dialog';
@@ -196,25 +195,15 @@ export class SpAssetOverviewComponent implements OnInit {
additionalData: {},
labelIds: [],
};
- const dialogRef = this.dialogService.open(
- SpCreateAssetDialogComponent,
+ this.router.navigate(
+ ['assets', 'details', assetModel.elementId, 'edit'],
{
- panelType: PanelType.SLIDE_IN_PANEL,
- title: this.translateService.instant('Create asset'),
- width: '50vw',
- data: {
- assetModel: assetModel,
+ state: {
+ assetModel,
+ isNewAsset: true,
},
},
);
-
- dialogRef.afterClosed().subscribe(ev => {
- if (ev) {
- this.loadAssets();
- this.assetBrowserService.refreshBrowserAssetData();
- this.goToDetailsView(assetModel, true);
- }
- });
}
goToDetailsView(asset: AssetSummaryDto, editMode = false) {
diff --git
a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html
b/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html
deleted file mode 100644
index 67c2d43431..0000000000
--- a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!--
-~ Licensed to the Apache Software Foundation (ASF) under one or more
-~ contributor license agreements. See the NOTICE file distributed with
-~ this work for additional information regarding copyright ownership.
-~ The ASF licenses this file to You 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.
-~
--->
-
-<div class="sp-dialog-container">
- <div class="sp-dialog-content p-15">
- <div fxFlex="100">
- <div
- fxFlex="100"
- fxLayout="column"
- style="margin: 5px; width: 100%"
- >
- <sp-form-field [level]="2" [label]="'Asset Name' | translate">
- <mat-form-field>
- <input
- matInput
- data-cy="asset-name"
- [(ngModel)]="assetModel.assetName"
- />
- </mat-form-field>
- </sp-form-field>
- <sp-form-field [level]="2" [label]="'Description' | translate">
- <mat-form-field>
- <input
- matInput
- [(ngModel)]="assetModel.assetDescription"
- />
- </mat-form-field>
- </sp-form-field>
- </div>
- </div>
- </div>
- <mat-divider></mat-divider>
- <div class="sp-dialog-actions actions-align-left" fxLayoutGap="10px">
- <button
- mat-button
- mat-flat-button
- color="accent"
- data-cy="create-asset-panel"
- (click)="onSave()"
- >
- {{ 'Create' | translate }}
- </button>
-
- <button
- mat-button
- mat-flat-button
- class="mat-basic mr-10"
- (click)="onCancel()"
- style="margin-right: 10px"
- >
- {{ 'Close' | translate }}
- </button>
- </div>
-</div>
diff --git
a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts
b/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts
deleted file mode 100644
index e7747f8fa0..0000000000
--- a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 { Component, Input, inject } from '@angular/core';
-import {
- AssetManagementService,
- SpAssetModel,
-} from '@streampipes/platform-services';
-import { DialogRef } from '@streampipes/shared-ui';
-import {
- FlexDirective,
- LayoutDirective,
- LayoutGapDirective,
-} from '@ngbracket/ngx-layout/flex';
-import { FormFieldComponent } from '@streampipes/shared-ui';
-import { MatFormField } from '@angular/material/form-field';
-import { MatInput } from '@angular/material/input';
-import { FormsModule } from '@angular/forms';
-import { MatDivider } from '@angular/material/divider';
-import { MatButton } from '@angular/material/button';
-import { TranslatePipe } from '@ngx-translate/core';
-
-@Component({
- selector: 'sp-create-asset-dialog-component',
- templateUrl: './create-asset-dialog.component.html',
- imports: [
- FlexDirective,
- LayoutDirective,
- FormFieldComponent,
- MatFormField,
- MatInput,
- FormsModule,
- MatDivider,
- LayoutGapDirective,
- MatButton,
- TranslatePipe,
- ],
-})
-export class SpCreateAssetDialogComponent {
- private dialogRef =
- inject<DialogRef<SpCreateAssetDialogComponent>>(DialogRef);
- private assetManagementService = inject(AssetManagementService);
-
- @Input() assetModel: SpAssetModel;
-
- onCancel(): void {
- this.dialogRef.close();
- }
-
- onSave(): void {
- this.assetManagementService
- .createAsset(this.assetModel)
- .subscribe(() => {
- this.dialogRef.close(true);
- });
- }
-}