Merge pull request #11073 from AllForNothing/permission

Swith to new API for recent log page
This commit is contained in:
Will Sun 2020-03-17 11:25:29 +08:00 committed by GitHub
commit 2b6fb4abcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 132 additions and 146 deletions

View File

@ -456,7 +456,9 @@
"FILTER_PLACEHOLDER": "Filter Logs", "FILTER_PLACEHOLDER": "Filter Logs",
"INVALID_DATE": "Invalid date.", "INVALID_DATE": "Invalid date.",
"OF": "of", "OF": "of",
"NOT_FOUND": "We couldn't find any logs!" "NOT_FOUND": "We couldn't find any logs!",
"RESOURCE": "Resource",
"RESOURCE_TYPE": "Resource Type"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "Yes", "YES": "Yes",

View File

@ -456,7 +456,9 @@
"FILTER_PLACEHOLDER": "Filtrar logs", "FILTER_PLACEHOLDER": "Filtrar logs",
"INVALID_DATE": "Fecha invalida.", "INVALID_DATE": "Fecha invalida.",
"OF": "of", "OF": "of",
"NOT_FOUND": "No pudimos encontrar ningún registro!" "NOT_FOUND": "No pudimos encontrar ningún registro!",
"RESOURCE": "Resource",
"RESOURCE_TYPE": "Resource Type"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "Yes", "YES": "Yes",

View File

@ -448,7 +448,9 @@
"FILTER_PLACEHOLDER": "Filtrer les Logs", "FILTER_PLACEHOLDER": "Filtrer les Logs",
"INVALID_DATE": "Date invalide.", "INVALID_DATE": "Date invalide.",
"OF": "de", "OF": "de",
"NOT_FOUND": "Nous n'avons trouvé aucun journal!" "NOT_FOUND": "Nous n'avons trouvé aucun journal!",
"RESOURCE": "Resource",
"RESOURCE_TYPE": "Resource Type"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "Yes", "YES": "Yes",

View File

@ -454,7 +454,9 @@
"FILTER_PLACEHOLDER": "Filtrar Logs", "FILTER_PLACEHOLDER": "Filtrar Logs",
"INVALID_DATE": "Data inválida.", "INVALID_DATE": "Data inválida.",
"OF": "de", "OF": "de",
"NOT_FOUND": "Nós não encontramos nenhum registro!" "NOT_FOUND": "Nós não encontramos nenhum registro!",
"RESOURCE": "Resource",
"RESOURCE_TYPE": "Resource Type"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "Yes", "YES": "Yes",

View File

@ -455,7 +455,10 @@
"ITEMS": "adet", "ITEMS": "adet",
"FILTER_PLACEHOLDER": "Günlükleri Filtrele", "FILTER_PLACEHOLDER": "Günlükleri Filtrele",
"INVALID_DATE": "Geçersiz tarih.", "INVALID_DATE": "Geçersiz tarih.",
"OF": "of" "OF": "of",
"NOT_FOUND": "We couldn't find any logs!",
"RESOURCE": "Resource",
"RESOURCE_TYPE": "Resource Type"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "Evet", "YES": "Evet",

View File

@ -455,7 +455,9 @@
"FILTER_PLACEHOLDER": "过滤日志", "FILTER_PLACEHOLDER": "过滤日志",
"INVALID_DATE": "无效日期。", "INVALID_DATE": "无效日期。",
"OF": "共计", "OF": "共计",
"NOT_FOUND": "未发现任何日志!" "NOT_FOUND": "未发现任何日志!",
"RESOURCE": "资源",
"RESOURCE_TYPE": "资源类型"
}, },
"REPLICATION": { "REPLICATION": {
"YES": "是", "YES": "是",

View File

@ -6,8 +6,8 @@
<div class="select filter-tag clr-select-wrapper" [hidden]="!isOpenFilterTag"> <div class="select filter-tag clr-select-wrapper" [hidden]="!isOpenFilterTag">
<select id="selectKey" (change)="selectFilterKey($event)"> <select id="selectKey" (change)="selectFilterKey($event)">
<option value="username">{{"AUDIT_LOG.USERNAME" | translate | lowercase}}</option> <option value="username">{{"AUDIT_LOG.USERNAME" | translate | lowercase}}</option>
<option value="repository">{{"CONFIG.REPOSITORY" | translate | lowercase}}</option> <option value="resource">{{"AUDIT_LOG.RESOURCE" | translate | lowercase}}</option>
<option value="tag">{{"REPOSITORY.TAG" | translate | lowercase}}</option> <option value="resourceType">{{"AUDIT_LOG.RESOURCE_TYPE" | translate | lowercase}}</option>
<option value="operation">{{"AUDIT_LOG.OPERATION" | translate | lowercase}}</option> <option value="operation">{{"AUDIT_LOG.OPERATION" | translate | lowercase}}</option>
</select> </select>
</div> </div>
@ -20,17 +20,17 @@
</div> </div>
</div> </div>
<div> <div>
<clr-datagrid (clrDgRefresh)="load($event)" [clrDgLoading]="loading"> <clr-datagrid (clrDgRefresh)="load()" [clrDgLoading]="loading">
<clr-dg-column>{{'AUDIT_LOG.USERNAME' | translate}}</clr-dg-column> <clr-dg-column>{{'AUDIT_LOG.USERNAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.REPOSITORY_NAME' | translate}}</clr-dg-column> <clr-dg-column>{{'AUDIT_LOG.RESOURCE' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.TAGS' | translate}}</clr-dg-column> <clr-dg-column>{{'AUDIT_LOG.RESOURCE_TYPE' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.OPERATION' | translate}}</clr-dg-column> <clr-dg-column>{{'AUDIT_LOG.OPERATION' | translate}}</clr-dg-column>
<clr-dg-column>{{'AUDIT_LOG.TIMESTAMP' | translate}}</clr-dg-column> <clr-dg-column>{{'AUDIT_LOG.TIMESTAMP' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{ 'AUDIT_LOG.NOT_FOUND' | translate }}</clr-dg-placeholder> <clr-dg-placeholder>{{ 'AUDIT_LOG.NOT_FOUND' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let l of recentLogs"> <clr-dg-row *ngFor="let l of recentLogs">
<clr-dg-cell>{{l.username}}</clr-dg-cell> <clr-dg-cell>{{l.username}}</clr-dg-cell>
<clr-dg-cell>{{l.repo_name}}</clr-dg-cell> <clr-dg-cell>{{l.resource}}</clr-dg-cell>
<clr-dg-cell>{{l.repo_tag}}</clr-dg-cell> <clr-dg-cell>{{l.resource_type}}</clr-dg-cell>
<clr-dg-cell>{{l.operation}}</clr-dg-cell> <clr-dg-cell>{{l.operation}}</clr-dg-cell>
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell> <clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>

View File

@ -1,41 +1,79 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core'; import { DebugElement } from '@angular/core';
import { AccessLog, AccessLogItem, RequestQueryParams } from '../../services';
import { RecentLogComponent } from './recent-log.component'; import { RecentLogComponent } from './recent-log.component';
import { AccessLogService, AccessLogDefaultService } from '../../services/access-log.service';
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config'; import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
import { ErrorHandler } from '../../utils/error-handler'; import { ErrorHandler } from '../../utils/error-handler';
import { SharedModule } from '../../utils/shared/shared.module'; import { SharedModule } from '../../utils/shared/shared.module';
import { FilterComponent } from '../filter/filter.component'; import { FilterComponent } from '../filter/filter.component';
import { click, CURRENT_BASE_HREF } from '../../utils/utils'; import { click, CURRENT_BASE_HREF } from '../../utils/utils';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { delay } from 'rxjs/operators'; import { AuditLog } from "../../../../ng-swagger-gen/models/audit-log";
import { AuditlogService } from "../../../../ng-swagger-gen/services/auditlog.service";
import { HttpHeaders, HttpResponse } from "@angular/common/http";
import ListAuditLogsParams = AuditlogService.ListAuditLogsParams;
import { delay } from "rxjs/operators";
describe('RecentLogComponent (inline template)', () => { describe('RecentLogComponent (inline template)', () => {
let component: RecentLogComponent; let component: RecentLogComponent;
let fixture: ComponentFixture<RecentLogComponent>; let fixture: ComponentFixture<RecentLogComponent>;
let serviceConfig: IServiceConfig; let serviceConfig: IServiceConfig;
let logService: AccessLogService; let auditlogService: AuditlogService;
let spy: jasmine.Spy;
let mockItems: AccessLogItem[] = [];
let mockData: AccessLog = {
metadata: {
xTotalCount: 18
},
data: []
};
let mockData2: AccessLog = {
metadata: {
xTotalCount: 1
},
data: []
};
let testConfig: IServiceConfig = { let testConfig: IServiceConfig = {
logBaseEndpoint: CURRENT_BASE_HREF + "/logs/testing" logBaseEndpoint: CURRENT_BASE_HREF + "/logs/testing"
}; };
const fakedErrorHandler = {
error() {
return undefined;
}
};
const mockedAuditLogs: AuditLog [] = [];
for (let i = 0; i < 18; i++) {
let item: AuditLog = {
id: 23 + i,
resource: "myproject/demo" + i,
resource_type: "N/A",
operation: "create",
op_time: "2017-04-11T10:26:22Z",
username: "user91" + i
};
mockedAuditLogs.push(item);
}
const fakedAuditlogService = {
listAuditLogsResponse(params: ListAuditLogsParams) {
if (params && params.username) {
if (params.username === 'demo0') {
return of(new HttpResponse({
body: mockedAuditLogs.slice(0, 1),
headers: new HttpHeaders({
"x-total-count": "18"
})
})).pipe(delay(0));
}
return of(new HttpResponse({
body: mockedAuditLogs,
headers: new HttpHeaders({
"x-total-count": "18"
})
})).pipe(delay(0));
} else {
if (params.page === 1) {
return of(new HttpResponse({
body: mockedAuditLogs.slice(0, 15),
headers: new HttpHeaders({
"x-total-count": "18"
})
})).pipe(delay(0));
} else {
return of(new HttpResponse({
body: mockedAuditLogs.slice(15),
headers: new HttpHeaders({
"x-total-count": "18"
})
})).pipe(delay(0));
}
}
}
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@ -43,9 +81,9 @@ describe('RecentLogComponent (inline template)', () => {
], ],
declarations: [FilterComponent, RecentLogComponent], declarations: [FilterComponent, RecentLogComponent],
providers: [ providers: [
ErrorHandler, { provide: ErrorHandler, useValue: fakedErrorHandler },
{ provide: AuditlogService, useValue: fakedAuditlogService },
{ provide: SERVICE_CONFIG, useValue: testConfig }, { provide: SERVICE_CONFIG, useValue: testConfig },
{ provide: AccessLogService, useClass: AccessLogDefaultService }
] ]
}); });
@ -55,38 +93,7 @@ describe('RecentLogComponent (inline template)', () => {
fixture = TestBed.createComponent(RecentLogComponent); fixture = TestBed.createComponent(RecentLogComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
serviceConfig = TestBed.get(SERVICE_CONFIG); serviceConfig = TestBed.get(SERVICE_CONFIG);
logService = fixture.debugElement.injector.get(AccessLogService); auditlogService = fixture.debugElement.injector.get(AuditlogService);
// Mock data
for (let i = 0; i < 18; i++) {
let item: AccessLogItem = {
log_id: 23 + i,
user_id: 45 + i,
project_id: 11 + i,
repo_name: "myproject/demo" + i,
repo_tag: "N/A",
operation: "create",
op_time: "2017-04-11T10:26:22Z",
username: "user91" + i
};
mockItems.push(item);
}
mockData2.data = mockItems.slice(0, 1);
mockData.data = mockItems;
spy = spyOn(logService, 'getRecentLogs')
.and.callFake(function (params: RequestQueryParams) {
if (params && params.get('username')) {
return of(mockData2);
} else {
if (params.get('page') === '1') {
mockData.data = mockItems.slice(0, 15);
} else {
mockData.data = mockItems.slice(15, 18);
}
return of(mockData).pipe(delay(0));
}
});
fixture.detectChanges(); fixture.detectChanges();
}); });
@ -100,15 +107,11 @@ describe('RecentLogComponent (inline template)', () => {
}); });
it('should get data from AccessLogService', async(() => { it('should get data from AccessLogService', async(() => {
expect(logService).toBeTruthy(); expect(auditlogService).toBeTruthy();
expect(spy.calls.any()).toBe(true, 'getRecentLogs called');
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { // wait for async getRecentLogs fixture.whenStable().then(() => { // wait for async getRecentLogs
fixture.detectChanges(); fixture.detectChanges();
expect(component.recentLogs).toBeTruthy(); expect(component.recentLogs).toBeTruthy();
expect(component.logsCache).toBeTruthy();
expect(component.recentLogs.length).toEqual(15); expect(component.recentLogs.length).toEqual(15);
}); });
})); }));
@ -125,28 +128,16 @@ describe('RecentLogComponent (inline template)', () => {
expect(el.textContent.trim()).toEqual('user910'); expect(el.textContent.trim()).toEqual('user910');
}); });
})); }));
it('should support pagination', async () => {
// Will fail after upgrade to angular 6. todo: need to fix it. fixture.autoDetectChanges(true);
xit('should support pagination', () => { await fixture.whenStable();
let el: HTMLButtonElement = fixture.nativeElement.querySelector('.pagination-next');
expect(el).toBeTruthy();
el.click();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable();
fixture.whenStable().then(() => { expect(component.currentPage).toEqual(2);
fixture.detectChanges(); expect(component.recentLogs.length).toEqual(3);
let el: HTMLButtonElement = fixture.nativeElement.querySelector('.pagination-next');
expect(el).toBeTruthy();
el.click();
jasmine.clock().tick(100);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let els: HTMLElement[] = fixture.nativeElement.querySelectorAll('.datagrid-row');
expect(els).toBeTruthy();
expect(els.length).toEqual(4);
});
});
}); });
it('should support filtering list by keywords', async(() => { it('should support filtering list by keywords', async(() => {
@ -154,22 +145,17 @@ describe('RecentLogComponent (inline template)', () => {
let el: HTMLElement = fixture.nativeElement.querySelector('.search-btn'); let el: HTMLElement = fixture.nativeElement.querySelector('.search-btn');
expect(el).toBeTruthy("Not found search icon"); expect(el).toBeTruthy("Not found search icon");
click(el); click(el);
fixture.detectChanges(); fixture.detectChanges();
let el2: HTMLInputElement = fixture.nativeElement.querySelector('input'); let el2: HTMLInputElement = fixture.nativeElement.querySelector('input');
expect(el2).toBeTruthy("Not found input"); expect(el2).toBeTruthy("Not found input");
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
component.doFilter("demo0"); component.doFilter("demo0");
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.recentLogs).toBeTruthy(); expect(component.recentLogs).toBeTruthy();
expect(component.logsCache).toBeTruthy();
expect(component.recentLogs.length).toEqual(1); expect(component.recentLogs.length).toEqual(1);
}); });
}); });
@ -177,32 +163,23 @@ describe('RecentLogComponent (inline template)', () => {
it('should support refreshing', async(() => { it('should support refreshing', async(() => {
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
let el: HTMLButtonElement = fixture.nativeElement.querySelector('.pagination-next'); let el: HTMLButtonElement = fixture.nativeElement.querySelector('.pagination-next');
expect(el).toBeTruthy(); expect(el).toBeTruthy();
el.click(); el.click();
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.recentLogs).toBeTruthy(); expect(component.recentLogs).toBeTruthy();
expect(component.logsCache).toBeTruthy();
expect(component.recentLogs.length).toEqual(3); expect(component.recentLogs.length).toEqual(3);
let refreshEl: HTMLElement = fixture.nativeElement.querySelector(".refresh-btn"); let refreshEl: HTMLElement = fixture.nativeElement.querySelector(".refresh-btn");
expect(refreshEl).toBeTruthy("Not found refresh button"); expect(refreshEl).toBeTruthy("Not found refresh button");
refreshEl.click(); refreshEl.click();
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.recentLogs).toBeTruthy(); expect(component.recentLogs).toBeTruthy();
expect(component.logsCache).toBeTruthy();
expect(component.recentLogs.length).toEqual(15); expect(component.recentLogs.length).toEqual(15);
}); });

View File

@ -12,20 +12,11 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { Comparator, State } from '../../services/interface';
import {
AccessLogService,
AccessLog,
AccessLogItem,
RequestQueryParams
} from '../../services';
import { ErrorHandler } from '../../utils/error-handler'; import { ErrorHandler } from '../../utils/error-handler';
import { CustomComparator } from '../../utils/utils';
import {
DEFAULT_PAGE_SIZE,
} from '../../utils/utils';
import { finalize } from "rxjs/operators"; import { finalize } from "rxjs/operators";
import { AuditlogService } from "../../../../ng-swagger-gen/services/auditlog.service";
import { AuditLog } from "../../../../ng-swagger-gen/models/audit-log";
import ListAuditLogsParams = AuditlogService.ListAuditLogsParams;
@Component({ @Component({
selector: 'hbr-log', selector: 'hbr-log',
@ -34,8 +25,7 @@ import { finalize } from "rxjs/operators";
}) })
export class RecentLogComponent implements OnInit { export class RecentLogComponent implements OnInit {
recentLogs: AccessLogItem[] = []; recentLogs: AuditLog[] = [];
logsCache: AccessLog;
loading: boolean = true; loading: boolean = true;
currentTerm: string; currentTerm: string;
defaultFilter = "username"; defaultFilter = "username";
@ -43,17 +33,13 @@ export class RecentLogComponent implements OnInit {
@Input() withTitle: boolean = false; @Input() withTitle: boolean = false;
pageSize: number = 15; pageSize: number = 15;
currentPage: number = 1; // Double bound to pagination component currentPage: number = 1; // Double bound to pagination component
totalCount: number = 0;
constructor( constructor(
private logService: AccessLogService, private logService: AuditlogService,
private errorHandler: ErrorHandler) { } private errorHandler: ErrorHandler) { }
ngOnInit(): void { ngOnInit(): void {
} }
public get totalCount(): number {
return this.logsCache && this.logsCache.metadata ? this.logsCache.metadata.xTotalCount : 0;
}
public get inProgress(): boolean { public get inProgress(): boolean {
return this.loading; return this.loading;
} }
@ -66,7 +52,8 @@ export class RecentLogComponent implements OnInit {
this.currentTerm = terms.trim(); this.currentTerm = terms.trim();
this.loading = true; this.loading = true;
this.currentPage = 1; this.currentPage = 1;
this.load({page: {}}); this.totalCount = 0;
this.load();
} }
public refresh(): void { public refresh(): void {
@ -74,11 +61,7 @@ export class RecentLogComponent implements OnInit {
} }
openFilter(isOpen: boolean): void { openFilter(isOpen: boolean): void {
if (isOpen) { this.isOpenFilterTag = isOpen;
this.isOpenFilterTag = true;
} else {
this.isOpenFilterTag = false;
}
} }
selectFilterKey($event: any): void { selectFilterKey($event: any): void {
@ -86,21 +69,27 @@ export class RecentLogComponent implements OnInit {
this.doFilter(this.currentTerm); this.doFilter(this.currentTerm);
} }
load(state) { load() {
if (!state || !state.page) {
return;
}
// Keep it for future filter // Keep it for future filter
// this.currentState = state; // this.currentState = state;
let params: RequestQueryParams = new RequestQueryParams().set("page", '' + this.currentPage).set("page_size", '' + this.pageSize); const params: ListAuditLogsParams = {
page: this.currentPage,
pageSize: this.pageSize
};
if (this.currentTerm && this.currentTerm !== "") { if (this.currentTerm && this.currentTerm !== "") {
params = params.set(this.defaultFilter, this.currentTerm); params[this.defaultFilter] = this.currentTerm;
} }
this.loading = true; this.loading = true;
this.logService.getRecentLogs(params).pipe(finalize(() => (this.loading = false))) this.logService.listAuditLogsResponse(params).pipe(finalize(() => (this.loading = false)))
.subscribe(response => { .subscribe(response => {
this.logsCache = response; // Keep the data // Get total count
this.recentLogs = response.data; if (response.headers) {
let xHeader: string = response.headers.get("x-total-count");
if (xHeader) {
this.totalCount = parseInt(xHeader, 0);
}
}
this.recentLogs = response.body as AuditLog[];
}, error => { }, error => {
this.errorHandler.error(error); this.errorHandler.error(error);
}); });

View File

@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable, forkJoin, of, throwError as observableThrowError } from "rxjs"; import { Observable, forkJoin} from "rxjs";
import { map, tap, publishReplay, refCount } from "rxjs/operators"; import { map, share } from "rxjs/operators";
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { CacheObservable } from "../utils/cache-util"; import { CacheObservable } from "../utils/cache-util";
import { CURRENT_BASE_HREF } from "../utils/utils"; import { CURRENT_BASE_HREF } from "../utils/utils";
@ -46,6 +46,8 @@ export abstract class UserPermissionService {
// @dynamic // @dynamic
@Injectable() @Injectable()
export class UserPermissionDefaultService extends UserPermissionService { export class UserPermissionDefaultService extends UserPermissionService {
// to prevent duplicate permissions HTTP requests
private _sharedPermissionObservableMap: {[key: string]: Observable<Array<Permission>>} = {};
constructor( constructor(
private http: HttpClient, private http: HttpClient,
) { ) {
@ -55,7 +57,12 @@ export class UserPermissionDefaultService extends UserPermissionService {
@CacheObservable({ maxAge: 1000 * 60 }) @CacheObservable({ maxAge: 1000 * 60 })
private getPermissions(scope: string, relative?: boolean): Observable<Array<Permission>> { private getPermissions(scope: string, relative?: boolean): Observable<Array<Permission>> {
const url = `${ CURRENT_BASE_HREF }/users/current/permissions?scope=${scope}&relative=${relative ? 'true' : 'false'}`; const url = `${ CURRENT_BASE_HREF }/users/current/permissions?scope=${scope}&relative=${relative ? 'true' : 'false'}`;
return this.http.get<Array<Permission>>(url); if (this._sharedPermissionObservableMap[url]) {
return this._sharedPermissionObservableMap[url];
} else {
this._sharedPermissionObservableMap[url] = this.http.get<Array<Permission>>(url).pipe(share());
return this._sharedPermissionObservableMap[url];
}
} }
private hasPermission(permission: Permission, scope: string, relative?: boolean): Observable<boolean> { private hasPermission(permission: Permission, scope: string, relative?: boolean): Observable<boolean> {