mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-01-03 03:26:42 +00:00
bugfix, Permission Link display correctly
This commit is contained in:
parent
b6b9d735f5
commit
fc0fdd2e51
@ -12,7 +12,7 @@
|
||||
*/
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {Server} from "@models/server";
|
||||
import {ApiInformationService} from "@services/api-information.service";
|
||||
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
|
||||
import {Methods, Permission, PermissionActions} from "@models/api/permission";
|
||||
import {PermissionsService} from "@services/permissions.service";
|
||||
import {ToasterService} from "@services/toaster.service";
|
||||
|
@ -11,14 +11,14 @@
|
||||
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||
*/
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {IFormatedList} from "@services/api-information.service";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
@Pipe({
|
||||
name: 'filterComplete'
|
||||
})
|
||||
export class FilterCompletePipe implements PipeTransform {
|
||||
|
||||
transform(value: IFormatedList[], searchText: string): IFormatedList[] {
|
||||
transform(value: IGenericApiObject[], searchText: string): IGenericApiObject[] {
|
||||
if (!searchText || searchText === '') { return value; }
|
||||
|
||||
return value.filter((v) => {
|
||||
|
@ -11,10 +11,11 @@
|
||||
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||
*/
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {ApiInformationService, IFormatedList} from "@services/api-information.service";
|
||||
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
|
||||
import {Server} from "@models/server";
|
||||
import {PermissionPath} from "@components/permissions-management/add-permission-line/path-auto-complete/PermissionPath";
|
||||
import {SubPath} from "@components/permissions-management/add-permission-line/path-auto-complete/SubPath";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
@Component({
|
||||
selector: 'app-path-auto-complete',
|
||||
@ -28,7 +29,7 @@ export class PathAutoCompleteComponent implements OnInit {
|
||||
@Input() server: Server;
|
||||
path: PermissionPath = new PermissionPath();
|
||||
values: string[] = [];
|
||||
private completeData: { data: IFormatedList[]; key: string };
|
||||
private completeData: { data: IGenericApiObject[]; key: string };
|
||||
public mode: 'SELECT' | 'COMPLETE' | undefined;
|
||||
completeField: string;
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {map, switchMap} from "rxjs/operators";
|
||||
import {forkJoin, Observable, of} from "rxjs";
|
||||
import {ApiInformationService} from "@services/api-information.service";
|
||||
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
|
||||
import {Server} from "@models/server";
|
||||
|
||||
@Pipe({
|
||||
|
@ -13,7 +13,7 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {Methods, Permission} from "@models/api/permission";
|
||||
import {Server} from '@models/server';
|
||||
import {ApiInformationService} from "@services/api-information.service";
|
||||
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
|
||||
import {PermissionsService} from "@services/permissions.service";
|
||||
import {ToasterService} from "@services/toaster.service";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
|
@ -19,7 +19,8 @@ import {Permission} from "@models/api/permission";
|
||||
import {AddPermissionLineComponent} from "@components/permissions-management/add-permission-line/add-permission-line.component";
|
||||
import {ServerService} from "@services/server.service";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
import {ApiInformationService, IFormatedList} from "@services/api-information.service";
|
||||
import {ApiInformationService} from "@services/ApiInformation/api-information.service";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
@Component({
|
||||
selector: 'app-permissions-management',
|
||||
@ -33,7 +34,7 @@ export class PermissionsManagementComponent implements OnInit {
|
||||
newPermissionEdit = false;
|
||||
searchPermissions: any;
|
||||
pageEvent: PageEvent | undefined;
|
||||
filteredOptions: IFormatedList[];
|
||||
filteredOptions: IGenericApiObject[];
|
||||
options: string[] = [];
|
||||
|
||||
@ViewChild('dynamic', {
|
||||
|
@ -10,15 +10,14 @@
|
||||
*
|
||||
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||
*/
|
||||
import {Component, Inject, Input, OnInit, Output} from '@angular/core';
|
||||
import {Role} from "@models/api/role";
|
||||
import {Component, Input, OnInit, Output, EventEmitter} from '@angular/core';
|
||||
import {Server} from "@models/server";
|
||||
import {Permission} from "@models/api/permission";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {PermissionEditorValidateDialogComponent} from "@components/role-management/role-detail/permission-editor/permission-editor-validate-dialog/permission-editor-validate-dialog.component";
|
||||
import { EventEmitter } from '@angular/core';
|
||||
import {ApiInformationService, IFormatedList} from "@services/api-information.service";
|
||||
import {ApiInformationService } from "@services/ApiInformation/api-information.service";
|
||||
import {PageEvent} from "@angular/material/paginator";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
@Component({
|
||||
selector: 'app-permission-editor',
|
||||
@ -30,7 +29,7 @@ export class PermissionEditorComponent implements OnInit {
|
||||
owned: Set<Permission>;
|
||||
available: Set<Permission>;
|
||||
searchPermissions: any;
|
||||
filteredOptions: IFormatedList[];
|
||||
filteredOptions: IGenericApiObject[];
|
||||
pageEventOwned: PageEvent | undefined;
|
||||
pageEventAvailable: PageEvent | undefined;
|
||||
|
||||
@ -106,10 +105,10 @@ export class PermissionEditorComponent implements OnInit {
|
||||
}
|
||||
|
||||
get ownedArray() {
|
||||
return Array.from(this.owned.values())
|
||||
return Array.from(this.owned.values());
|
||||
}
|
||||
|
||||
get availableArray() {
|
||||
return Array.from(this.available.values())
|
||||
return Array.from(this.available.values());
|
||||
}
|
||||
}
|
||||
|
43
src/app/services/ApiInformation/ApiInformationCache.ts
Normal file
43
src/app/services/ApiInformation/ApiInformationCache.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {IApiData} from "./IApiData";
|
||||
import {Server} from "../../models/server";
|
||||
import {IExtraParams} from "./IExtraParams";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
export class ApiInformationCache {
|
||||
|
||||
private cache = new Map<string, IApiData>();
|
||||
|
||||
|
||||
public update(server: Server, key: string, value: string, extraParams: IExtraParams[], data: IGenericApiObject[]) {
|
||||
const dataKey = this.generateKey(server, key, value, extraParams);
|
||||
this.cache.set(dataKey, {expired: Date.now() + 10000, data});
|
||||
}
|
||||
|
||||
public get(server: Server, key: string, value: string, extraParams: IExtraParams[]): IGenericApiObject[] | undefined {
|
||||
const dataKey = this.generateKey(server, key, value, extraParams);
|
||||
const data = this.cache.get(dataKey);
|
||||
if (data) {
|
||||
if (data.expired > Date.now()) {
|
||||
return data.data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private generateKey(server: Server, key: string, value: string, extraParams: IExtraParams[]) {
|
||||
return `${server.id}-${key}-${value}-${extraParams.map(param => `${param.key}+${param.value}`).join('.')}`;
|
||||
}
|
||||
|
||||
searchByName(name: string) {
|
||||
const result: IGenericApiObject[] = [];
|
||||
this.cache.forEach((apiData: IApiData) => {
|
||||
apiData.data.forEach((value: IGenericApiObject) => {
|
||||
if (value.name.includes(name)) {
|
||||
result.push(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
106
src/app/services/ApiInformation/GetObjectIdHelper.ts
Normal file
106
src/app/services/ApiInformation/GetObjectIdHelper.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import {ApiInformationService, IApiObject} from "@services/ApiInformation/api-information.service";
|
||||
import {Server} from "@models/server";
|
||||
import {IExtraParams} from "@services/ApiInformation/IExtraParams";
|
||||
import {forkJoin, Observable, of} from "rxjs";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
import {map} from "rxjs/operators";
|
||||
|
||||
export class GetObjectIdHelper {
|
||||
|
||||
public static getIdNameFromKey(key: string): string {
|
||||
return /{([^)]+)}/.exec(key)[1];
|
||||
}
|
||||
|
||||
public static findElementInObjectListFn(key): (data: IApiObject[]) => IApiObject {
|
||||
return function findElement(data: IApiObject[]): IApiObject {
|
||||
const elem = data.find(d => d.name === key);
|
||||
if (!elem) {
|
||||
throw new Error('entry not found');
|
||||
}
|
||||
return elem;
|
||||
};
|
||||
}
|
||||
|
||||
public static buildRequestURL(server: Server, value: string, extraParams: IExtraParams[]): (elem) => string {
|
||||
return (elem): string => {
|
||||
let url = `${server.protocol}//${server.host}:${server.port}${elem.path}`;
|
||||
if (extraParams) {
|
||||
extraParams.forEach((param) => {
|
||||
url = url.replace(param.key, param.value);
|
||||
});
|
||||
}
|
||||
|
||||
if (value) {
|
||||
url = `${url}/${value}`;
|
||||
}
|
||||
return url;
|
||||
};
|
||||
}
|
||||
|
||||
public static createResponseObject(key: string,
|
||||
extraParams: IExtraParams[],
|
||||
service: ApiInformationService,
|
||||
server: Server
|
||||
): (response) => Observable<IGenericApiObject[]> {
|
||||
|
||||
const idName = key ? GetObjectIdHelper.getIdNameFromKey(key) : undefined;
|
||||
return (response): Observable<IGenericApiObject[]> => {
|
||||
|
||||
if (!(response instanceof Array)) {
|
||||
response = [response];
|
||||
}
|
||||
if (response.length === 0) {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
/*
|
||||
specific treatment for link_id
|
||||
*/
|
||||
if (key === '{link_id}') {
|
||||
return GetObjectIdHelper.setLinkObjectInformation(response, extraParams, service, server);
|
||||
} else {
|
||||
return GetObjectIdHelper.setGenericObjectInformation(response, idName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static setGenericObjectInformation(response: any[], idName: string): Observable<IGenericApiObject[]> {
|
||||
const keys = Object.keys(response[0]);
|
||||
const idKey = keys.find(k => k.match(/_id$|filename/));
|
||||
const nameKey = keys.find(k => k.match(/name/));
|
||||
response = response.map(o => {
|
||||
return {
|
||||
id: o[idName] || o[idKey],
|
||||
name: o[nameKey]
|
||||
};
|
||||
});
|
||||
return of(response);
|
||||
}
|
||||
|
||||
private static setLinkObjectInformation(links: any[],
|
||||
extraParams: IExtraParams[],
|
||||
service: ApiInformationService,
|
||||
server: Server
|
||||
): Observable<IGenericApiObject[]> {
|
||||
|
||||
return forkJoin(links.map(link => GetObjectIdHelper.getLinkInformation(link, extraParams, service, server)));
|
||||
}
|
||||
|
||||
private static getLinkInformation(link: any,
|
||||
extraParams: IExtraParams[],
|
||||
service: ApiInformationService,
|
||||
server: Server
|
||||
): Observable<IGenericApiObject> {
|
||||
|
||||
const nodesDataObs = link.nodes.map(node => service.getListByObjectId(server, '{node_id}', node.node_id, extraParams));
|
||||
return forkJoin(nodesDataObs)
|
||||
.pipe(map((nodes: [any]) => {
|
||||
const name = nodes
|
||||
.reduce((acc, val) => acc.concat(val), [])
|
||||
.map(node => node.name)
|
||||
.join(' <-> ');
|
||||
|
||||
return {id: link.link_id, name};
|
||||
}));
|
||||
}
|
||||
}
|
6
src/app/services/ApiInformation/IApiData.ts
Normal file
6
src/app/services/ApiInformation/IApiData.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
export interface IApiData {
|
||||
expired: number;
|
||||
data: IGenericApiObject[];
|
||||
}
|
4
src/app/services/ApiInformation/IExtraParams.ts
Normal file
4
src/app/services/ApiInformation/IExtraParams.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IExtraParams {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
4
src/app/services/ApiInformation/IGenericApiObject.ts
Normal file
4
src/app/services/ApiInformation/IGenericApiObject.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export interface IGenericApiObject {
|
||||
id: string;
|
||||
name?: string;
|
||||
}
|
@ -13,10 +13,14 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {Observable, of, ReplaySubject} from "rxjs";
|
||||
import {map, switchMap, take} from "rxjs/operators";
|
||||
import {Methods} from "@models/api/permission";
|
||||
import {HttpServer} from "@services/http-server.service";
|
||||
import {Server} from "@models/server";
|
||||
import {map, switchMap, take, tap} from "rxjs/operators";
|
||||
import {Methods} from "app/models/api/permission";
|
||||
import {HttpServer} from "app/services/http-server.service";
|
||||
import {Server} from "app/models/server";
|
||||
import {GetObjectIdHelper} from "@services/ApiInformation/GetObjectIdHelper";
|
||||
import {IExtraParams} from "@services/ApiInformation/IExtraParams";
|
||||
import {ApiInformationCache} from "@services/ApiInformation/ApiInformationCache";
|
||||
import {IGenericApiObject} from "@services/ApiInformation/IGenericApiObject";
|
||||
|
||||
export interface IPathDict {
|
||||
methods: ('POST' | 'GET' | 'PUT' | 'DELETE' | 'HEAD' | 'PATH')[];
|
||||
@ -35,18 +39,12 @@ export interface IQueryObject {
|
||||
text: string[];
|
||||
}
|
||||
|
||||
export interface IFormatedList {
|
||||
id: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiInformationService {
|
||||
|
||||
private cache_permissions: { [key: string]: IFormatedList; } = {};
|
||||
private cache = new ApiInformationCache();
|
||||
private allowed = ['projects', 'images', 'templates', 'computes', 'symbols', 'notifications'];
|
||||
private data: ReplaySubject<IPathDict[]> = new ReplaySubject<IPathDict[]>(1);
|
||||
private objs: ReplaySubject<IApiObject[]> = new ReplaySubject<IApiObject[]>(1);
|
||||
@ -206,71 +204,24 @@ export class ApiInformationService {
|
||||
}));
|
||||
}
|
||||
|
||||
getListByObjectId(server: Server, key: string, value?: string, extraParams?: { key: string; value: string }[]) {
|
||||
const idName = /{([^)]+)}/.exec(key)[1];
|
||||
function findElement(data: IApiObject[]): IApiObject {
|
||||
const elem = data.find(d => d.name === key);
|
||||
if (!elem) {
|
||||
throw new Error('entry not found');
|
||||
}
|
||||
return elem;
|
||||
getListByObjectId(server: Server, key: string, value?: string, extraParams?: IExtraParams[]) {
|
||||
|
||||
const cachedData = this.cache.get(server, key, value, extraParams);
|
||||
if (cachedData) {
|
||||
return of(cachedData);
|
||||
}
|
||||
|
||||
return this.objs.pipe(
|
||||
map(findElement),
|
||||
switchMap(elem => {
|
||||
let url = `${server.protocol}//${server.host}:${server.port}${elem.path}`;
|
||||
if (extraParams) {
|
||||
extraParams.forEach((param) => {
|
||||
url = url.replace(param.key, param.value);
|
||||
});
|
||||
}
|
||||
|
||||
if (value) {
|
||||
url = `${url}/${value}`;
|
||||
}
|
||||
|
||||
return this.httpClient.get<any[]>(url, {headers: {Authorization: `Bearer ${server.authToken}`}});
|
||||
}
|
||||
),
|
||||
switchMap(response => {
|
||||
|
||||
if (response instanceof Array) {
|
||||
if (response.length === 0) {
|
||||
return of([]);
|
||||
}
|
||||
const keys = Object.keys(response[0]);
|
||||
const idKey = keys.find(k => k.match(/_id$|filename/));
|
||||
const nameKey = keys.find(k => k.match(/name/));
|
||||
response = response.map(o => {
|
||||
return {
|
||||
id: o[idName] || o[idKey],
|
||||
name: o[nameKey]
|
||||
};
|
||||
});
|
||||
response.forEach(elt => {
|
||||
this.cache_permissions[elt.id] = elt;
|
||||
});
|
||||
return of(response);
|
||||
} else {
|
||||
const keys = Object.keys(response);
|
||||
const idKey = keys.find(k => k.match(/_id$|filename/));
|
||||
const nameKey = keys.find(k => k.match(/name/));
|
||||
const ret = {id: response[idName] || response[idKey], name: response[nameKey]};
|
||||
this.cache_permissions[ret.id] = ret;
|
||||
return of([ret]);
|
||||
}
|
||||
}), take(1));
|
||||
map(GetObjectIdHelper.findElementInObjectListFn(key)),
|
||||
map(GetObjectIdHelper.buildRequestURL(server, value, extraParams)),
|
||||
switchMap(url => this.httpClient.get<any[]>(url, {headers: {Authorization: `Bearer ${server.authToken}`}})),
|
||||
switchMap(GetObjectIdHelper.createResponseObject(key, extraParams, this, server)),
|
||||
tap(data => this.cache.update(server, key, value, extraParams, data)),
|
||||
take(1));
|
||||
}
|
||||
|
||||
getIdByObjNameFromCache(name: string): IFormatedList[] {
|
||||
const ret: IFormatedList[] = [];
|
||||
for (let [key, value] of Object.entries(this.cache_permissions)) {
|
||||
if (value.name.includes(name)) {
|
||||
ret.push(value);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
getIdByObjNameFromCache(name: string): IGenericApiObject[] {
|
||||
return this.cache.searchByName(name);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user