This is an automated email from the ASF dual-hosted git repository. dominikriemer pushed a commit to branch add-resource-dtos in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit 84eb3a158cca3bf272f0e98dc532ef9002596b89 Author: Dominik Riemer <[email protected]> AuthorDate: Fri Jun 12 22:32:45 2026 +0200 Add adapter summary dto --- .../model/connect/adapter/AdapterSummaryDto.java | 32 ++++++ .../management/AdapterResourceManager.java | 33 ++++++ .../rest/impl/connect/AdapterResource.java | 13 +++ .../src/lib/apis/adapter.service.ts | 35 ++++++- .../lib/model/resource/resource-summary.model.ts | 12 +++ .../existing-adapters.component.ts | 115 ++++++++++++--------- .../delete-adapter-dialog.component.ts | 8 +- .../all-adapter-actions-dialog.component.ts | 13 +-- ui/src/app/connect/filter/adapter-filter.pipe.ts | 13 ++- 9 files changed, 207 insertions(+), 67 deletions(-) diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/AdapterSummaryDto.java b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/AdapterSummaryDto.java new file mode 100644 index 0000000000..8158dd9636 --- /dev/null +++ b/streampipes-model/src/main/java/org/apache/streampipes/model/connect/adapter/AdapterSummaryDto.java @@ -0,0 +1,32 @@ +/* + * 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. + * + */ + +package org.apache.streampipes.model.connect.adapter; + +import java.util.List; + +public record AdapterSummaryDto(String elementId, + String correspondingDataStreamElementId, + String name, + String description, + boolean running, + long createdAt, + String appId, + List<String> includedAssets, + String icon) { +} diff --git a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java index 3e981cb9c3..f3ba00fc39 100644 --- a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java +++ b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java @@ -19,26 +19,53 @@ package org.apache.streampipes.resource.management; import org.apache.streampipes.commons.exceptions.connect.AdapterException; import org.apache.streampipes.model.connect.adapter.AdapterDescription; +import org.apache.streampipes.model.connect.adapter.AdapterSummaryDto; +import org.apache.streampipes.model.resource.ResourceSummaryDto; import org.apache.streampipes.model.util.Cloner; +import org.apache.streampipes.resource.management.permission.SpPermissionEvaluator; import org.apache.streampipes.resource.management.secret.SecretProvider; import org.apache.streampipes.storage.api.connect.IAdapterStorage; import org.apache.streampipes.storage.api.system.ICertificateStorage; import org.apache.streampipes.storage.management.StorageDispatcher; +import org.springframework.security.core.Authentication; + public class AdapterResourceManager extends AbstractResourceManager<IAdapterStorage> { private final ICertificateStorage certificateStorage; + private final SpPermissionEvaluator permissionEvaluator; public AdapterResourceManager(IAdapterStorage adapterStorage, ICertificateStorage certificateStorage) { super(adapterStorage); this.certificateStorage = certificateStorage; + this.permissionEvaluator = new SpPermissionEvaluator(); } public AdapterResourceManager() { super(StorageDispatcher.INSTANCE.getNoSqlStore() .getAdapterInstanceStorage()); this.certificateStorage = StorageDispatcher.INSTANCE.getNoSqlStore().getCertificateStorage(); + this.permissionEvaluator = new SpPermissionEvaluator(); + } + + public ResourceSummaryDto<AdapterSummaryDto> getSummary(Authentication auth) { + var adapters = db.findAll().stream() + .filter(adapter -> canReadAdapter(auth, adapter)) + .map(adapter -> new AdapterSummaryDto( + adapter.getElementId(), + adapter.getCorrespondingDataStreamElementId(), + adapter.getName(), + adapter.getDescription(), + adapter.isRunning(), + adapter.getCreatedAt(), + adapter.getAppId(), + adapter.getIncludedAssets(), + adapter.getIcon() + )) + .toList(); + + return new ResourceSummaryDto<>(adapters, adapters.size()); } /** @@ -98,4 +125,10 @@ public class AdapterResourceManager extends AbstractResourceManager<IAdapterStor return encryptedAdapterDescription; } + private boolean canReadAdapter(Authentication auth, AdapterDescription adapter) { + return adapter != null + && adapter.getCorrespondingDataStreamElementId() != null + && permissionEvaluator.hasPermission(auth, adapter.getCorrespondingDataStreamElementId(), "READ"); + } + } diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java index 692b49ae9a..689a739033 100644 --- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java +++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/connect/AdapterResource.java @@ -29,12 +29,15 @@ import org.apache.streampipes.manager.pipeline.PipelineManager; import org.apache.streampipes.model.client.user.DefaultRole; import org.apache.streampipes.model.client.user.Permission; import org.apache.streampipes.model.connect.adapter.AdapterDescription; +import org.apache.streampipes.model.connect.adapter.AdapterSummaryDto; import org.apache.streampipes.model.connect.adapter.PipelineUpdateInfo; import org.apache.streampipes.model.connect.adapter.compact.CompactAdapter; import org.apache.streampipes.model.message.Message; import org.apache.streampipes.model.message.Notifications; import org.apache.streampipes.model.monitoring.SpLogMessage; +import org.apache.streampipes.model.resource.ResourceSummaryDto; import org.apache.streampipes.model.util.ElementIdGenerator; +import org.apache.streampipes.resource.management.AdapterResourceManager; import org.apache.streampipes.resource.management.PermissionResourceManager; import org.apache.streampipes.resource.management.SpResourceManager; import org.apache.streampipes.resource.management.permission.SpPermissionEvaluator; @@ -339,10 +342,20 @@ public class AdapterResource extends AbstractAdapterResource<AdapterMasterManage return managementService.getAllAdapterInstances(); } + @GetMapping(path = "/summary", produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("this.hasReadAuthority()") + public ResourceSummaryDto<AdapterSummaryDto> getAdapterSummary() { + return getResourceManager().getSummary(getAuthentication()); + } + private AdapterDescription getAdapterDescription(String elementId) throws AdapterException { return managementService.getAdapter(elementId); } + private AdapterResourceManager getResourceManager() { + return getSpResourceManager().manageAdapters(); + } + private CompactAdapter toCompactAdapterDescription(AdapterDescription adapterDescription) throws Exception { return new CompactAdapterManagement(List.of()).convertToCompactAdapter(adapterDescription); } diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/adapter.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/adapter.service.ts index 8cebfd8263..8d00991f56 100644 --- a/ui/projects/streampipes/platform-services/src/lib/apis/adapter.service.ts +++ b/ui/projects/streampipes/platform-services/src/lib/apis/adapter.service.ts @@ -28,6 +28,10 @@ import { Message, PipelineUpdateInfo, } from '../model/gen/streampipes-model'; +import { + AdapterSummaryDto, + ResourceSummaryDto, +} from '../model/resource/resource-summary.model'; @Injectable({ providedIn: 'root', @@ -48,6 +52,12 @@ export class AdapterService { return this.requestAdapterDescriptions('/master/adapters'); } + getAdapterSummary(): Observable<ResourceSummaryDto<AdapterSummaryDto>> { + return this.http.get<ResourceSummaryDto<AdapterSummaryDto>>( + `${this.connectPath}/master/adapters/summary`, + ); + } + getAdapter(id: string): Observable<AdapterDescription> { return this.http .get(this.connectPath + `/master/adapters/${id}`) @@ -78,10 +88,17 @@ export class AdapterService { stopAdapter( adapter: AdapterDescription, forceStop = false, + ): Observable<Message> { + return this.stopAdapterByElementId(adapter.elementId, forceStop); + } + + stopAdapterByElementId( + elementId: string, + forceStop = false, ): Observable<Message> { return this.http .post( - this.adapterMasterUrl + adapter.elementId + '/stop', + this.adapterMasterUrl + elementId + '/stop', {}, { params: { forceStop } }, ) @@ -134,16 +151,26 @@ export class AdapterService { deleteAdapter( adapter: AdapterDescription, deleteAssociatedPipelines: boolean, + ): Observable<any> { + return this.deleteAdapterById( + adapter.elementId, + deleteAssociatedPipelines, + ); + } + + deleteAdapterById( + elementId: string, + deleteAssociatedPipelines: boolean, ): Observable<any> { return this.deleteRequest( - adapter, + elementId, deleteAssociatedPipelines, 'master/adapters', ); } private deleteRequest( - adapter: AdapterDescription, + elementId: string, deleteAssociatedPipelines: boolean, url: string, ) { @@ -151,7 +178,7 @@ export class AdapterService { ? '?deleteAssociatedPipelines=true' : ''; return this.http.delete( - `${this.connectPath}/${url}/${adapter.elementId}${queryString}`, + `${this.connectPath}/${url}/${elementId}${queryString}`, ); } diff --git a/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts b/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts index 9e714179a4..4e3b8b3aef 100644 --- a/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts +++ b/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts @@ -32,6 +32,18 @@ export interface DashboardSummaryDto { lastModifiedEpochMs: number | null; } +export interface AdapterSummaryDto { + elementId: string; + correspondingDataStreamElementId: string; + name: string; + description: string; + running: boolean; + createdAt: number; + appId: string; + includedAssets: string[]; + icon: string; +} + export interface PipelineSummaryDto { elementId: string; name: string; diff --git a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts index a639664909..01b82347ad 100644 --- a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts +++ b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.ts @@ -18,9 +18,9 @@ import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { - AdapterDescription, AdapterMonitoringService, AdapterService, + AdapterSummaryDto, PipelineElementAssetService, SpLogMessage, SpMetricsEntry, @@ -110,8 +110,8 @@ import { DatePipe } from '@angular/common'; ], }) export class ExistingAdaptersComponent implements OnInit, OnDestroy { - existingAdapters: AdapterDescription[] = []; - filteredAdapters: AdapterDescription[] = []; + existingAdapters: AdapterSummaryDto[] = []; + filteredAdapters: AdapterSummaryDto[] = []; currentFilter: AdapterFilterSettingsModel; operationInProgressAdapterId: string | undefined; @@ -135,7 +135,7 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { resourceIdKey: 'elementId', }; - dataSource: MatTableDataSource<AdapterDescription> = + dataSource: MatTableDataSource<AdapterSummaryDto> = new MatTableDataSource(); adapterMetrics: Record<string, SpMetricsEntry> = {}; @@ -193,34 +193,46 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { }; } - startAdapter(adapter: AdapterDescription) { + startAdapter(adapter: AdapterSummaryDto): void { this.operationInProgressAdapterId = adapter.elementId; - this.adapterService.startAdapter(adapter).subscribe( - _ => { - this.getAdaptersRunning(); - }, - error => { - this.operationInProgressAdapterId = undefined; - this.openAdapterStatusErrorDialog(adapter, error.error, true); - }, - ); + this.adapterService + .startAdapterByElementId(adapter.elementId) + .subscribe( + _ => { + this.getAdaptersRunning(); + }, + error => { + this.operationInProgressAdapterId = undefined; + this.openAdapterStatusErrorDialog( + adapter, + error.error, + true, + ); + }, + ); } - stopAdapter(adapter: AdapterDescription, forceStop = false) { + stopAdapter(adapter: AdapterSummaryDto, forceStop = false): void { this.operationInProgressAdapterId = adapter.elementId; - this.adapterService.stopAdapter(adapter, forceStop).subscribe( - _ => { - this.getAdaptersRunning(); - }, - error => { - this.operationInProgressAdapterId = undefined; - this.openAdapterStatusErrorDialog(adapter, error.error, false); - }, - ); + this.adapterService + .stopAdapterByElementId(adapter.elementId, forceStop) + .subscribe( + _ => { + this.getAdaptersRunning(); + }, + error => { + this.operationInProgressAdapterId = undefined; + this.openAdapterStatusErrorDialog( + adapter, + error.error, + false, + ); + }, + ); } startStopSelectedAdapters( - event: SpTableMultiActionExecuteEvent<AdapterDescription>, + event: SpTableMultiActionExecuteEvent<AdapterSummaryDto>, ) { if (event.action !== 'start' && event.action !== 'stop') { return; @@ -253,10 +265,10 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { } openAdapterStatusErrorDialog( - adapter: AdapterDescription, + adapter: AdapterSummaryDto, message: SpLogMessage, startAction: boolean, - ) { + ): void { const title = startAction ? this.startAdapterErrorText : this.stopAdapterErrorText; @@ -283,7 +295,7 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { }); } - getMonitoringInfos(adapters: AdapterDescription[]) { + getMonitoringInfos(adapters: AdapterSummaryDto[]): void { const filteredElementIds = adapters.map(adapter => adapter.elementId); this.adapterMonitoringService @@ -293,7 +305,7 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { }); } - getIconUrl(adapter: AdapterDescription) { + getIconUrl(adapter: AdapterSummaryDto): string | undefined { if (adapter.includedAssets?.some(asset => asset.startsWith('icon.'))) { return ( this.pipelineElementAssetService.getAssetUrl(adapter.appId) + @@ -302,7 +314,7 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { } } - showPermissionsDialog(adapter: AdapterDescription) { + showPermissionsDialog(adapter: AdapterSummaryDto): void { const dialogRef = this.dialogService.open( ObjectPermissionDialogComponent, { @@ -330,11 +342,11 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { * Start edit mode * @param adapter */ - editAdapter(adapter: AdapterDescription) { + editAdapter(adapter: AdapterSummaryDto): void { this.router.navigate(['connect', 'edit', adapter.elementId]); } - deleteAdapter(adapter: AdapterDescription): void { + deleteAdapter(adapter: AdapterSummaryDto): void { const dialogRef: DialogRef<DeleteAdapterDialogComponent> = this.dialogService.open(DeleteAdapterDialogComponent, { panelType: PanelType.STANDARD_PANEL, @@ -353,12 +365,12 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { } getAdaptersRunning(): void { - this.adapterService.getAdapters().subscribe(adapters => { - this.existingAdapters = adapters; + this.adapterService.getAdapterSummary().subscribe(adapterSummary => { + this.existingAdapters = adapterSummary.resources; this.existingAdapters.sort((a, b) => a.name.localeCompare(b.name)); this.applyAdapterFilters(this.currentFilterIds); this.operationInProgressAdapterId = undefined; - this.getMonitoringInfos(adapters); + this.getMonitoringInfos(this.existingAdapters); setTimeout(() => { this.dataSource.sort = this.sort; }); @@ -367,21 +379,24 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { applyAdapterFilters(elementIds: Set<string>): void { this.currentFilterIds = elementIds; - this.filteredAdapters = this.adapterFilter - .transform(this.existingAdapters, this.currentFilter) - .filter(a => { - if (elementIds === undefined) { - return false; - } else if (elementIds.size === 0) { - return true; - } else { - return elementIds.has(a.elementId); - } - }); + this.filteredAdapters = ( + this.adapterFilter.transform( + this.existingAdapters, + this.currentFilter, + ) as AdapterSummaryDto[] + ).filter(a => { + if (elementIds === undefined) { + return false; + } else if (elementIds.size === 0) { + return true; + } else { + return elementIds.has(a.elementId); + } + }); this.dataSource.data = this.filteredAdapters; } - startAdapterTutorial() { + startAdapterTutorial(): void { this.shepherdService.startAdapterTour(); } @@ -391,18 +406,18 @@ export class ExistingAdaptersComponent implements OnInit, OnDestroy { }); } - applyFilter(filter: AdapterFilterSettingsModel) { + applyFilter(filter: AdapterFilterSettingsModel): void { this.currentFilter = filter; if (this.dataSource) { this.applyAdapterFilters(this.currentFilterIds); } } - navigateToDetailsOverviewPage(adapter: AdapterDescription): void { + navigateToDetailsOverviewPage(adapter: AdapterSummaryDto): void { this.router.navigate(['connect', 'details', adapter.elementId]); } - ngOnDestroy() { + ngOnDestroy(): void { this.user$?.unsubscribe(); this.tutorial$?.unsubscribe(); this.assetFilter$?.unsubscribe(); diff --git a/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.ts b/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.ts index f1bff10041..0bfce6884e 100644 --- a/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.ts +++ b/ui/src/app/connect/dialog/delete-adapter-dialog/delete-adapter-dialog.component.ts @@ -19,6 +19,7 @@ import { Component, Input, inject } from '@angular/core'; import { AdapterDescription, + AdapterSummaryDto, AdapterService, } from '@streampipes/platform-services'; import { DialogRef } from '@streampipes/shared-ui'; @@ -52,7 +53,7 @@ export class DeleteAdapterDialogComponent { private dataMarketplaceService = inject(AdapterService); @Input() - adapter: AdapterDescription; + adapter: AdapterDescription | AdapterSummaryDto; isInProgress = false; currentStatus: any; @@ -71,7 +72,10 @@ export class DeleteAdapterDialogComponent { this.deleteAssociatedPipelines = deleteAssociatedPipelines; this.dataMarketplaceService - .deleteAdapter(this.adapter, deleteAssociatedPipelines) + .deleteAdapterById( + this.adapter.elementId, + deleteAssociatedPipelines, + ) .subscribe( _data => { this.close(true); diff --git a/ui/src/app/connect/dialog/start-all-adapters/all-adapter-actions-dialog.component.ts b/ui/src/app/connect/dialog/start-all-adapters/all-adapter-actions-dialog.component.ts index dd74983228..67a651f329 100644 --- a/ui/src/app/connect/dialog/start-all-adapters/all-adapter-actions-dialog.component.ts +++ b/ui/src/app/connect/dialog/start-all-adapters/all-adapter-actions-dialog.component.ts @@ -20,6 +20,7 @@ import { Component, Input, OnInit, inject } from '@angular/core'; import { DialogRef } from '@streampipes/shared-ui'; import { AdapterDescription, + AdapterSummaryDto, AdapterService, } from '@streampipes/platform-services'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; @@ -39,9 +40,9 @@ export class AllAdapterActionsComponent implements OnInit { private translate = inject(TranslateService); @Input() - adapters: AdapterDescription[]; + adapters: Array<AdapterDescription | AdapterSummaryDto>; - adaptersToModify: AdapterDescription[]; + adaptersToModify: Array<AdapterDescription | AdapterSummaryDto>; actionStatus: any; actionFinished: boolean; page: string; @@ -89,7 +90,7 @@ export class AllAdapterActionsComponent implements OnInit { }); } - initiateAction(adapter: AdapterDescription, index) { + initiateAction(adapter: AdapterDescription | AdapterSummaryDto, index) { this.actionRunning = true; this.actionStatus.push({ name: adapter.name, @@ -99,10 +100,10 @@ export class AllAdapterActionsComponent implements OnInit { this.runAdapterAction(adapter, index); } - runAdapterAction(adapter: AdapterDescription, index) { + runAdapterAction(adapter: AdapterDescription | AdapterSummaryDto, index) { const observable = this.action - ? this.adapterService.startAdapter(adapter) - : this.adapterService.stopAdapter(adapter); + ? this.adapterService.startAdapterByElementId(adapter.elementId) + : this.adapterService.stopAdapterByElementId(adapter.elementId); observable .subscribe(data => { this.actionStatus[index].status = data.success diff --git a/ui/src/app/connect/filter/adapter-filter.pipe.ts b/ui/src/app/connect/filter/adapter-filter.pipe.ts index d594dc8d02..1381c1dcf7 100644 --- a/ui/src/app/connect/filter/adapter-filter.pipe.ts +++ b/ui/src/app/connect/filter/adapter-filter.pipe.ts @@ -17,7 +17,10 @@ */ import { Injectable, Pipe, PipeTransform } from '@angular/core'; -import { AdapterDescription } from '@streampipes/platform-services'; +import { + AdapterDescription, + AdapterSummaryDto, +} from '@streampipes/platform-services'; import { AdapterFilterSettingsModel } from '../model/adapter-filter-settings.model'; @Pipe({ name: 'adapterFilter' }) @@ -26,9 +29,9 @@ export class AdapterFilterPipe implements PipeTransform { constructor() {} transform( - adapterDescriptions: AdapterDescription[], + adapterDescriptions: Array<AdapterDescription | AdapterSummaryDto>, activeFilters: AdapterFilterSettingsModel, - ): AdapterDescription[] { + ): Array<AdapterDescription | AdapterSummaryDto> { if (!activeFilters) { return adapterDescriptions; } else { @@ -39,7 +42,7 @@ export class AdapterFilterPipe implements PipeTransform { } private meetsFilterCondition( - adapterDescription: AdapterDescription, + adapterDescription: AdapterDescription | AdapterSummaryDto, activeFilters: AdapterFilterSettingsModel, ): boolean { return this.meetsFilterTextCondition( @@ -49,7 +52,7 @@ export class AdapterFilterPipe implements PipeTransform { } private meetsFilterTextCondition( - adapterDescription: AdapterDescription, + adapterDescription: AdapterDescription | AdapterSummaryDto, filterTerm: string, ): boolean { if (filterTerm === undefined || filterTerm === '') {
