Fix: refactor username pipe to better handle async loading

This commit is contained in:
shamoon 2025-01-25 09:44:52 -08:00
parent 8f9a294529
commit 1eaa17f104
7 changed files with 57 additions and 25 deletions

View File

@ -7309,7 +7309,7 @@
</context-group> </context-group>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/pipes/username.pipe.ts</context> <context context-type="sourcefile">src/app/pipes/username.pipe.ts</context>
<context context-type="linenumber">33</context> <context context-type="linenumber">37</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2332107018974972998" datatype="html"> <trans-unit id="2332107018974972998" datatype="html">

View File

@ -70,7 +70,7 @@
} }
@case (DisplayField.OWNER) { @case (DisplayField.OWNER) {
@if (doc.owner) { @if (doc.owner) {
<a class="btn-link text-dark text-decoration-none" type="button" (click)="clickOwner(doc.owner, $event)" title="Filter by owner" i18n-title>{{doc.owner | username}}</a> <a class="btn-link text-dark text-decoration-none" type="button" (click)="clickOwner(doc.owner, $event)" title="Filter by owner" i18n-title>{{doc.owner | username | async}}</a>
} }
} }
@case (DisplayField.ASN) { @case (DisplayField.ASN) {

View File

@ -136,7 +136,7 @@
} }
@if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
<div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center"> <div class="list-group-item bg-light text-dark p-1 border-0 d-flex align-items-center">
<i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username}}</small> <i-bs width=".9em" height=".9em" class="me-2 text-muted" name="person-fill-lock"></i-bs><small>{{document.owner | username | async}}</small>
</div> </div>
} }
@if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) { @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {

View File

@ -116,7 +116,7 @@
@if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) {
<div class="ps-0 p-1"> <div class="ps-0 p-1">
<i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs> <i-bs width="1em" height="1em" class="me-2 text-muted" name="person-fill-lock"></i-bs>
<small>{{document.owner | username}}</small> <small>{{document.owner | username | async}}</small>
</div> </div>
} }
@if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) { @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) {

View File

@ -318,7 +318,7 @@
} }
@if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) { @if (activeDisplayFields.includes(DisplayField.OWNER) && permissionService.currentUserCan(PermissionAction.View, PermissionType.User)) {
<td> <td>
{{d.owner | username}} {{d.owner | username | async}}
</td> </td>
} }
@if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) { @if (activeDisplayFields.includes(DisplayField.NOTES) && notesEnabled) {

View File

@ -38,6 +38,13 @@ describe('UsernamePipe', () => {
}) })
it('should transform user id to username', () => { it('should transform user id to username', () => {
let username
const assign = (name) => {
username = name
}
pipe.transform(2).subscribe(assign)
const req = httpTestingController.expectOne( const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}users/?page=1&page_size=100000` `${environment.apiBaseUrl}users/?page=1&page_size=100000`
) )
@ -55,24 +62,44 @@ describe('UsernamePipe', () => {
}, },
], ],
}) })
let username = pipe.transform(2)
expect(username).toEqual('username2') expect(username).toEqual('username2')
username = pipe.transform(3) pipe.transform(3).subscribe(assign)
expect(username).toEqual('User Name3') expect(username).toEqual('User Name3')
username = pipe.transform(4) pipe.transform(4).subscribe(assign)
expect(username).toEqual('') expect(username).toEqual('')
}) })
it('should show generic label when no users retrieved', () => { it('should show generic label when insufficient permissions', () => {
let username
const assign = (name) => {
username = name
}
jest
.spyOn(permissionsService, 'currentUserCan')
.mockImplementation((action, type) => {
return false
})
pipe.transform(4).subscribe(assign)
httpTestingController.expectNone(
`${environment.apiBaseUrl}users/?page=1&page_size=100000`
)
expect(username).toEqual('Shared')
})
it('should show empty string when no users retrieved due to error', () => {
let username
const assign = (name) => {
username = name
}
pipe.transform(4).subscribe(assign)
const req = httpTestingController.expectOne( const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}users/?page=1&page_size=100000` `${environment.apiBaseUrl}users/?page=1&page_size=100000`
) )
req.flush(null) req.error(new ProgressEvent('error'))
let username = pipe.transform(4) expect(username).toEqual('')
expect(username).toEqual('Shared')
}) })
}) })

View File

@ -1,9 +1,10 @@
import { Pipe, PipeTransform } from '@angular/core' import { Pipe, PipeTransform } from '@angular/core'
import { catchError, map, Observable, of } from 'rxjs'
import { User } from '../data/user' import { User } from '../data/user'
import { import {
PermissionAction, PermissionAction,
PermissionType,
PermissionsService, PermissionsService,
PermissionType,
} from '../services/permissions.service' } from '../services/permissions.service'
import { UserService } from '../services/rest/user.service' import { UserService } from '../services/rest/user.service'
@ -14,25 +15,29 @@ export class UsernamePipe implements PipeTransform {
users: User[] users: User[]
constructor( constructor(
permissionsService: PermissionsService, private permissionsService: PermissionsService,
userService: UserService private userService: UserService
) { ) {}
transform(userID: number): Observable<string> {
if ( if (
permissionsService.currentUserCan( this.permissionsService.currentUserCan(
PermissionAction.View, PermissionAction.View,
PermissionType.User PermissionType.User
) )
) { ) {
userService.listAll().subscribe((r) => (this.users = r.results)) return this.userService.listAll().pipe(
map((users) => {
this.users = users.results
return this.getName(this.users.find((u) => u.id === userID))
}),
catchError(() => of(''))
)
} else {
return of($localize`Shared`)
} }
} }
transform(userID: number): string {
return this.users
? (this.getName(this.users.find((u) => u.id === userID)) ?? '')
: $localize`Shared`
}
getName(user: User): string { getName(user: User): string {
if (!user) return '' if (!user) return ''
const name = [user.first_name, user.last_name].join(' ') const name = [user.first_name, user.last_name].join(' ')