This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch 
3798-add-creation-and-last-login-date-to-user-object
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to 
refs/heads/3798-add-creation-and-last-login-date-to-user-object by this push:
     new d353cc47ea feat(#3798): Show creation and last login date in user 
configuration view
d353cc47ea is described below

commit d353cc47eaec682ef8eacbbc9271631e2239803c
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Oct 1 09:54:01 2025 +0200

    feat(#3798): Show creation and last login date in user configuration view
---
 .../streampipes/model/client/user/UserAccount.java | 20 +++++++++++++++++
 .../resource/management/UserResourceManager.java   |  6 ++++-
 .../streampipes/rest/impl/Authentication.java      |  2 ++
 .../service/core/oauth2/UserService.java           |  4 +++-
 .../src/lib/model/gen/streampipes-model-client.ts  |  9 ++++++--
 .../src/lib/model/gen/streampipes-model.ts         |  3 ++-
 .../components/sp-label/sp-label.component.scss    |  2 +-
 .../lib/components/sp-label/sp-label.component.ts  |  6 ++---
 .../split-section/split-section.component.html     |  2 +-
 ui/src/app/configuration/configuration.module.ts   |  3 ++-
 .../abstract-security-principal-config.ts          | 17 +++++++-------
 .../role-configuration.component.html              |  1 +
 .../security-user-config.component.html            | 26 ++++++++++++++++++++++
 .../security-user-config.component.ts              | 18 +++++++++++++--
 .../existing-adapters.component.html               | 13 +++++------
 .../existing-adapters.component.scss               |  9 --------
 16 files changed, 103 insertions(+), 38 deletions(-)

diff --git 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
index 8fc20b0f0f..9b1dbcdf44 100644
--- 
a/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
+++ 
b/streampipes-model-client/src/main/java/org/apache/streampipes/model/client/user/UserAccount.java
@@ -42,6 +42,9 @@ public class UserAccount extends Principal {
   protected boolean darkMode = false;
   protected boolean hasAcknowledged = false;
 
+  protected long createdAtMillis;
+  protected long lastLoginAtMillis;
+
   /**
    * The authentication provider (LOCAL or one of the configured OAuth 
providers
    */
@@ -55,6 +58,7 @@ public class UserAccount extends Principal {
     this.preferredDataProcessors = new ArrayList<>();
     this.preferredDataSinks = new ArrayList<>();
     this.preferredDataStreams = new ArrayList<>();
+    this.createdAtMillis = System.currentTimeMillis();
     this.provider = UserAccount.LOCAL;
   }
 
@@ -190,4 +194,20 @@ public class UserAccount extends Principal {
   public void setHasAcknowledged(boolean hasAcknowledged) {
     this.hasAcknowledged = hasAcknowledged;
   }
+
+  public long getCreatedAtMillis() {
+    return createdAtMillis;
+  }
+
+  public void setCreatedAtMillis(long createdAtMillis) {
+    this.createdAtMillis = createdAtMillis;
+  }
+
+  public long getLastLoginAtMillis() {
+    return lastLoginAtMillis;
+  }
+
+  public void setLastLoginAtMillis(long lastLoginAtMillis) {
+    this.lastLoginAtMillis = lastLoginAtMillis;
+  }
 }
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
index 57cbd98437..343ecf0b3c 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/UserResourceManager.java
@@ -134,6 +134,10 @@ public class UserResourceManager extends 
AbstractResourceManager<IUserStorage> {
     }
   }
 
+  public void updateUser(Principal principal) {
+    db.updateUser(principal);
+  }
+
   private void createTokenAndSendActivationMail(String username) throws 
IOException {
     String activationCode = TokenUtil.generateToken(RECOVERY_TOKEN_LENGTH);
     storeActivationCode(username, activationCode);
@@ -194,7 +198,7 @@ public class UserResourceManager extends 
AbstractResourceManager<IUserStorage> {
   }
 
 
-  public void registerOauthUser(UserAccount userAccount) {
+  public void storeUser(UserAccount userAccount) {
     db.storeUser(userAccount);
   }
 }
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
index dc2d6d252a..ac61cd1939 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/Authentication.java
@@ -157,6 +157,8 @@ public class Authentication extends AbstractRestResource {
     Principal principal = ((PrincipalUserDetails<?>) 
auth.getPrincipal()).getDetails();
     if (principal instanceof UserAccount) {
       JwtAuthenticationResponse tokenResp = makeJwtResponse(auth);
+      ((UserAccount) 
principal).setLastLoginAtMillis(System.currentTimeMillis());
+      getSpResourceManager().manageUsers().updateUser(principal);
       return ok(tokenResp);
     } else {
       throw new BadCredentialsException("Could not create auth token");
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
index 5f766659c8..a692e3a503 100755
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/oauth2/UserService.java
@@ -92,11 +92,13 @@ public class UserService {
           );
         }
         applyRoles(user, oAuthConfig, attributes, false);
+        user.setLastLoginAtMillis(System.currentTimeMillis());
         userStorage.updateUser(user);
       } else {
         user = toUserAccount(registrationId, principalId, email, fullName);
+        user.setLastLoginAtMillis(System.currentTimeMillis());
         applyRoles(user, oAuthConfig, attributes, true);
-        new UserResourceManager().registerOauthUser(user);
+        new UserResourceManager().storeUser(user);
       }
 
       user = (UserAccount) userStorage.getUserById(principalId);
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
index f6532c9dbd..47ae72ca3e 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model-client.ts
@@ -16,12 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-09-09 
16:10:12.
+// Generated using typescript-generator version 3.2.1263 on 2025-10-01 
08:36:56.
 
-import { Storable } from '.streampipes-model';
+import { Storable } from './streampipes-model';
 
 export class Group implements Storable {
     alternateIds: string[];
@@ -240,11 +241,13 @@ export class ServiceAccount extends Principal {
 }
 
 export class UserAccount extends Principal {
+    createdAtMillis: number;
     darkMode: boolean;
     externallyManagedRoles: boolean;
     fullName: string;
     hasAcknowledged: boolean;
     hideTutorial: boolean;
+    lastLoginAtMillis: number;
     password: string;
     preferredDataProcessors: string[];
     preferredDataSinks: string[];
@@ -258,11 +261,13 @@ export class UserAccount extends Principal {
         }
         const instance = target || new UserAccount();
         super.fromData(data, instance);
+        instance.createdAtMillis = data.createdAtMillis;
         instance.darkMode = data.darkMode;
         instance.externallyManagedRoles = data.externallyManagedRoles;
         instance.fullName = data.fullName;
         instance.hasAcknowledged = data.hasAcknowledged;
         instance.hideTutorial = data.hideTutorial;
+        instance.lastLoginAtMillis = data.lastLoginAtMillis;
         instance.password = data.password;
         instance.preferredDataProcessors = __getCopyArrayFn(
             __identity<string>(),
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index f2df1c882b..6823c795ec 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -16,10 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-09-09 
16:10:11.
+// Generated using typescript-generator version 3.2.1263 on 2025-10-01 
08:36:50.
 
 export class NamedStreamPipesEntity implements Storable {
     '@class':
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
index a115d10892..432d26687c 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.scss
@@ -20,7 +20,7 @@
     border-radius: 15px;
     min-width: 50px;
     padding: 5px 7px;
-    border: 1px solid;
+    //border: 1px solid;
     display: inline-block;
     text-align: center;
 }
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
index 398b0f7982..8f52fb8d05 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/sp-label/sp-label.component.ts
@@ -27,7 +27,7 @@ import { SpColorizationService } from 
'../../services/colorization.service';
 })
 export class SpLabelComponent implements OnInit {
     @Input()
-    labelText: string;
+    labelText: string | number;
 
     @Input()
     small = false;
@@ -35,9 +35,9 @@ export class SpLabelComponent implements OnInit {
     @Input()
     size: 'small' | 'medium' | 'large' = 'large';
 
-    _labelBackground: string;
+    _labelBackground: string = 'var(--color-bg-2)';
 
-    labelTextColor = '';
+    labelTextColor = 'var(--color-default-text)';
     cssClass = '';
 
     constructor(private colorizationService: SpColorizationService) {}
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
 
b/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
index 54ec3df0de..be2e8d315c 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/components/split-section/split-section.component.html
@@ -18,7 +18,7 @@
 
 <div fxFlex="100" fxLayout="column">
     <div fxLayout="row">
-        <div fxFlex="300px" fxLayout="column" class="split-section">
+        <div fxFlex="250px" fxLayout="column" class="split-section">
             <div class="split-section-title">{{ title }}</div>
             <div class="split-section-description">{{ subtitle }}</div>
         </div>
diff --git a/ui/src/app/configuration/configuration.module.ts 
b/ui/src/app/configuration/configuration.module.ts
index af9f1d23e6..51f8856b7d 100644
--- a/ui/src/app/configuration/configuration.module.ts
+++ b/ui/src/app/configuration/configuration.module.ts
@@ -82,7 +82,7 @@ import { SpConfigurationLinkSettingsComponent } from 
'./general-configuration/li
 import { SitesConfigurationComponent } from 
'./sites-configuration/sites-configuration.component';
 import { LocationFeaturesConfigurationComponent } from 
'./sites-configuration/location-features-configuration/location-features-configuration.component';
 import { SiteAreaConfigurationComponent } from 
'./sites-configuration/site-area-configuration/site-area-configuration.component';
-import { MatSort } from '@angular/material/sort';
+import { MatSort, MatSortModule } from '@angular/material/sort';
 import { ManageSiteDialogComponent } from 
'./dialog/manage-site/manage-site-dialog.component';
 import { EditAssetLocationComponent } from 
'./dialog/manage-site/edit-location/edit-location.component';
 import { EditAssetLocationAreaComponent } from 
'./dialog/manage-site/edit-location/edit-location-area/edit-location-area.component';
@@ -129,6 +129,7 @@ import { SelectDataExportComponent } from 
'./dialog/data-retention-dialog/compon
         MatPaginatorModule,
         MatRadioModule,
         MatSelectModule,
+        MatSortModule,
         FormsModule,
         DragDropModule,
         CoreUiModule,
diff --git 
a/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
 
b/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
index b3f639b301..4cc91b1515 100644
--- 
a/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
+++ 
b/ui/src/app/configuration/security-configuration/abstract-security-principal-config.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Directive, OnInit, ViewChild } from '@angular/core';
+import { Directive, inject, OnInit, ViewChild } from '@angular/core';
 import { MatPaginator } from '@angular/material/paginator';
 import { MatSort } from '@angular/material/sort';
 import { MatTableDataSource } from '@angular/material/table';
@@ -42,18 +42,14 @@ export abstract class AbstractSecurityPrincipalConfig<
 {
     users: T[] = [];
 
-    @ViewChild(MatPaginator) paginator: MatPaginator;
-    pageSize = 1;
     @ViewChild(MatSort) sort: MatSort;
 
     dataSource: MatTableDataSource<T>;
 
-    constructor(
-        protected userService: UserService,
-        protected userAdminService: UserAdminService,
-        protected dialogService: DialogService,
-        private dialog: MatDialog,
-    ) {}
+    protected userService = inject(UserService);
+    protected userAdminService = inject(UserAdminService);
+    protected dialogService = inject(DialogService);
+    private dialog = inject(MatDialog);
 
     ngOnInit(): void {
         this.load();
@@ -88,6 +84,9 @@ export abstract class AbstractSecurityPrincipalConfig<
         this.getObservable().subscribe(response => {
             this.users = response;
             this.dataSource = new MatTableDataSource(this.users);
+            setTimeout(() => {
+                this.dataSource.sort = this.sort;
+            });
         });
     }
 
diff --git 
a/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
 
b/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
index 3d143fb285..b315eb152b 100644
--- 
a/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
+++ 
b/ui/src/app/configuration/security-configuration/role-configuration/role-configuration.component.html
@@ -32,6 +32,7 @@
             *ngIf="dataSource"
             [dataSource]="dataSource"
             [columns]="displayedColumns"
+            matSort
             data-cy="security-role-config"
         >
             <ng-container matColumnDef="roleName">
diff --git 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
index 91f1d4aee4..b8b2f26925 100644
--- 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
+++ 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.html
@@ -63,6 +63,32 @@
                 </td>
             </ng-container>
 
+            <ng-container matColumnDef="createdAtMillis">
+                <th mat-header-cell mat-sort-header *matHeaderCellDef>
+                    {{ 'Created' | translate }}
+                </th>
+                <td mat-cell *matCellDef="let account">
+                    {{
+                        this.dateFormatService.formatDate(
+                            account.createdAtMillis
+                        )
+                    }}
+                </td>
+            </ng-container>
+
+            <ng-container matColumnDef="lastLoginAtMillis">
+                <th mat-header-cell mat-sort-header *matHeaderCellDef>
+                    {{ 'Last Login' | translate }}
+                </th>
+                <td mat-cell *matCellDef="let account">
+                    {{
+                        this.dateFormatService.formatDate(
+                            account.lastLoginAtMillis
+                        )
+                    }}
+                </td>
+            </ng-container>
+
             <ng-container matColumnDef="edit">
                 <th mat-header-cell *matHeaderCellDef class="text-right">
                     Actions
diff --git 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
index fd4d232781..3326cf6d39 100644
--- 
a/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
+++ 
b/ui/src/app/configuration/security-configuration/security-user-configuration/security-user-config.component.ts
@@ -16,10 +16,11 @@
  *
  */
 
-import { Component } from '@angular/core';
+import { Component, inject } from '@angular/core';
 import { UserAccount } from '@streampipes/platform-services';
 import { AbstractSecurityPrincipalConfig } from 
'../abstract-security-principal-config';
 import { Observable } from 'rxjs';
+import { DateFormatService } from '@streampipes/shared-ui';
 
 @Component({
     selector: 'sp-security-user-config',
@@ -28,7 +29,16 @@ import { Observable } from 'rxjs';
     standalone: false,
 })
 export class SecurityUserConfigComponent extends 
AbstractSecurityPrincipalConfig<UserAccount> {
-    displayedColumns: string[] = ['username', 'provider', 'fullName', 'edit'];
+    displayedColumns: string[] = [
+        'username',
+        'provider',
+        'fullName',
+        'createdAtMillis',
+        'lastLoginAtMillis',
+        'edit',
+    ];
+
+    public dateFormatService = inject(DateFormatService);
 
     getObservable(): Observable<UserAccount[]> {
         return this.userAdminService.getAllUserAccounts();
@@ -43,4 +53,8 @@ export class SecurityUserConfigComponent extends 
AbstractSecurityPrincipalConfig
         user.provider = 'local';
         return user;
     }
+
+    formatDate(timestamp?: number): string {
+        return this.dateFormatService.formatDate(timestamp);
+    }
 }
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
index 73f8efa9cd..3f441fe4ef 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.html
@@ -210,14 +210,13 @@
                             #Messages
                         </th>
                         <td mat-cell *matCellDef="let adapter">
-                            <h5 class="monitoring-info">
-                                {{
+                            <sp-label
+                                size="small"
+                                [labelText]="
                                     adapterMetrics[adapter.elementId]
-                                        ? adapterMetrics[adapter.elementId]
-                                              .messagesOut.counter
-                                        : 0
-                                }}
-                            </h5>
+                                        ?.messagesOut.counter || 0
+                                "
+                            ></sp-label>
                         </td>
                     </ng-container>
 
diff --git 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
index a619e1a47a..52a48bdf92 100644
--- 
a/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
+++ 
b/ui/src/app/connect/components/existing-adapters/existing-adapters.component.scss
@@ -36,15 +36,6 @@
     padding-left: 10px;
 }
 
-.monitoring-info {
-    color: var(--color-default-text);
-    border: 1px solid var(--color-default-text);
-    background: var(--color-bg-0);
-    border-radius: 5px;
-    padding: 5px;
-    text-align: center;
-}
-
 .icon {
     max-width: 60px;
 }

Reply via email to