mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-06 01:01:33 +00:00
Permission management, create add permission component and apiInformation service, which parse swagger api information schema
This commit is contained in:
parent
2664911455
commit
65f1d45dc5
@ -300,6 +300,9 @@ import { PermissionEditorValidateDialogComponent } from './components/role-manag
|
||||
import { PermissionsManagementComponent } from './components/permissions-management/permissions-management.component';
|
||||
import { PermissionEditLineComponent } from '@components/permissions-management/permission-edit-line/permission-edit-line.component';
|
||||
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
|
||||
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||
import {PathAutoCompleteComponent} from './components/permissions-management/add-permission-line/path-auto-complete/path-auto-complete.component';
|
||||
import {FilterCompletePipe} from './components/permissions-management/add-permission-line/path-auto-complete/filter-complete.pipe';
|
||||
import { AddPermissionLineComponent } from './components/permissions-management/add-permission-line/add-permission-line.component';
|
||||
import { MethodButtonComponent } from './components/permissions-management/method-button/method-button.component';
|
||||
import { ActionButtonComponent } from './components/permissions-management/action-button/action-button.component';
|
||||
@ -523,7 +526,9 @@ import {MatFormFieldModule} from "@angular/material/form-field";
|
||||
AddPermissionLineComponent,
|
||||
MethodButtonComponent,
|
||||
ActionButtonComponent,
|
||||
DeletePermissionDialogComponent
|
||||
DeletePermissionDialogComponent,
|
||||
PathAutoCompleteComponent,
|
||||
FilterCompletePipe
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -537,16 +542,17 @@ import {MatFormFieldModule} from "@angular/material/form-field";
|
||||
NgxElectronModule,
|
||||
FileUploadModule,
|
||||
MatSidenavModule,
|
||||
MatFormFieldModule,
|
||||
ResizableModule,
|
||||
DragAndDropModule,
|
||||
DragDropModule,
|
||||
NgxChildProcessModule,
|
||||
MatFormFieldModule,
|
||||
MATERIAL_IMPORTS,
|
||||
NgCircleProgressModule.forRoot(),
|
||||
OverlayModule,
|
||||
MatSlideToggleModule,
|
||||
MatCheckboxModule,
|
||||
MatAutocompleteModule,
|
||||
],
|
||||
providers: [
|
||||
SettingsService,
|
||||
|
@ -1,19 +1,48 @@
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Type</mat-label>
|
||||
<mat-select [(ngModel)]="selectedType" (ngModelChange)="changeType($event)">
|
||||
<mat-option *ngFor="let type of objectTypes" [value]="type">{{type}}</mat-option>
|
||||
</mat-select>
|
||||
<div class="box-border">
|
||||
<div *ngIf="edit; else add">
|
||||
<div class="edit-mode">
|
||||
<div class="information-box">
|
||||
<div>
|
||||
<app-path-auto-complete
|
||||
[server]="server"
|
||||
(update)="permission.path = $event"></app-path-auto-complete>
|
||||
</div>
|
||||
<div class="methods">
|
||||
<app-action-button
|
||||
[disabled]="false"
|
||||
[action]="permission.action"></app-action-button>
|
||||
<div *ngFor="let method of apiInformation.getMethods(permission.path) | async">
|
||||
<app-method-button
|
||||
[name]="method"
|
||||
[disabled]="false"
|
||||
(update)="updateMethod($event)"></app-method-button>
|
||||
</div>
|
||||
<div class="description">
|
||||
<mat-form-field>
|
||||
<input
|
||||
[(ngModel)]="permission.description"
|
||||
matInput
|
||||
type="text"
|
||||
placeholder="Description"/>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-select>
|
||||
<mat-option *ngFor="let elt of elements" [value]="elt">{{elt.name ? elt.name : elt.filename}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<button mat-button matTooltip="Save Changes" (click)="onSave()" color="primary">
|
||||
<mat-icon>check_circle</mat-icon>
|
||||
</button>
|
||||
<button mat-button matTooltip="Cancel Changes" color="warn" (click)="onCancel()">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-box">
|
||||
<button mat-button (click)="reset()">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
<button mat-button (click)="save()">
|
||||
<mat-icon>done</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template #add>
|
||||
<div class="not-edit">
|
||||
<button mat-button (click)="edit = true">
|
||||
<mat-icon>add</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
@ -0,0 +1,49 @@
|
||||
.box-border {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.edit-mode {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.information-box {
|
||||
margin-left: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.information-box > div {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.methods {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.description > mat-form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.not-edit {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
@ -10,11 +10,12 @@
|
||||
*
|
||||
* Author: Sylvain MATHIEU, Elise LEBEAU
|
||||
*/
|
||||
import {Component, Input, OnInit, Output} from '@angular/core';
|
||||
import {ProjectService} from "@services/project.service";
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {Server} from "@models/server";
|
||||
import {ComputeService} from "@services/compute.service";
|
||||
import EventEmitter from "events";
|
||||
import {ApiInformationService} from "@services/api-information.service";
|
||||
import {Methods, Permission, PermissionActions} from "@models/api/permission";
|
||||
import {PermissionsService} from "@services/permissions.service";
|
||||
import {ToasterService} from "@services/toaster.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-permission-line',
|
||||
@ -23,51 +24,56 @@ import EventEmitter from "events";
|
||||
})
|
||||
export class AddPermissionLineComponent implements OnInit {
|
||||
|
||||
objectTypes = ['projects', 'images', 'templates', 'computes']
|
||||
elements = [];
|
||||
selectedType = 'projects';
|
||||
@Input() server: Server;
|
||||
@Output() addPermissionEvent = new EventEmitter<void>();
|
||||
permission: Permission = {
|
||||
action: PermissionActions.ALLOW,
|
||||
description: "",
|
||||
methods: [],
|
||||
path: "/",
|
||||
};
|
||||
edit = false;
|
||||
|
||||
@Output() addPermissionEvent = new EventEmitter();
|
||||
constructor(public apiInformation: ApiInformationService,
|
||||
private permissionService: PermissionsService,
|
||||
private toasterService: ToasterService) {
|
||||
|
||||
constructor(private projectService: ProjectService,
|
||||
private computeService: ComputeService) { }
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectService.list(this.server)
|
||||
.subscribe(elts => {
|
||||
this.elements = elts;
|
||||
})
|
||||
}
|
||||
|
||||
changeType(value) {
|
||||
console.log(value);
|
||||
this.selectedType = value;
|
||||
switch (this.selectedType) {
|
||||
case 'projects':
|
||||
this.projectService.list(this.server)
|
||||
.subscribe(elts => {
|
||||
this.elements = elts;
|
||||
})
|
||||
break;
|
||||
case 'computes':
|
||||
this.computeService.getComputes(this.server)
|
||||
.subscribe(elts => {
|
||||
this.elements = elts;
|
||||
})
|
||||
break;
|
||||
default:
|
||||
console.log("TODO");
|
||||
this.elements = [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
updateMethod(data: { name: Methods; enable: boolean }) {
|
||||
const set = new Set(this.permission.methods);
|
||||
if (data.enable) {
|
||||
set.add(data.name);
|
||||
} else {
|
||||
set.delete(data.name);
|
||||
}
|
||||
|
||||
onSave() {
|
||||
this.addPermissionEvent.emit('save');
|
||||
this.permission.methods = Array.from(set);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.addPermissionEvent.emit('cancel');
|
||||
reset() {
|
||||
this.permission = {
|
||||
action: PermissionActions.ALLOW,
|
||||
description: "",
|
||||
methods: [],
|
||||
path: "/",
|
||||
};
|
||||
|
||||
this.edit = false;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.permissionService.add(this.server, this.permission)
|
||||
.subscribe(() => {
|
||||
this.toasterService.success(`permission was created`);
|
||||
this.reset();
|
||||
}, (error) => {
|
||||
this.toasterService.error(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
import { FilterCompletePipe } from './filter-complete.pipe';
|
||||
|
||||
describe('FilterCompletePipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new FilterCompletePipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {IFormatedList} from "@services/api-information.service";
|
||||
|
||||
@Pipe({
|
||||
name: 'filterComplete'
|
||||
})
|
||||
export class FilterCompletePipe implements PipeTransform {
|
||||
|
||||
transform(value: IFormatedList[], searchText: string): IFormatedList[] {
|
||||
if (!searchText || searchText === '') { return value; }
|
||||
|
||||
return value.filter((v) => {
|
||||
return v.name.includes(searchText) || v.id.includes(searchText);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<div class="path">
|
||||
<div>Path: /</div>
|
||||
<div *ngFor="let p of path">{{p}}/</div>
|
||||
<div class="path-edit-line">
|
||||
<div>
|
||||
<div *ngIf="mode === 'SELECT'">
|
||||
<mat-select (valueChange)="valueChanged($event)" class="edit-area">
|
||||
<mat-option *ngFor="let value of values" value="{{value}}">{{value}}</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
<div *ngIf="mode === 'COMPLETE'">
|
||||
<input matInput
|
||||
autofocus
|
||||
class="complete edit-area"
|
||||
aria-label="find"
|
||||
[(ngModel)]="completeField"
|
||||
[matAutocomplete]="auto">
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<mat-option [value]="'*'">*</mat-option>
|
||||
<mat-option *ngFor="let data of completeData | filterComplete: completeField"
|
||||
[value]="data.id">
|
||||
<span>{{data.name}}</span> |
|
||||
<small>{{data.id}}</small>
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</div>
|
||||
</div>
|
||||
<div class="command-button">
|
||||
<mat-icon (click)="removePrevious()" *ngIf="path.length > 0">cancel</mat-icon>
|
||||
<mat-icon (click)="getNext()" *ngIf="!this.mode">add_circle_outline</mat-icon>
|
||||
<mat-icon
|
||||
matTooltip="validate data"
|
||||
(click)="validComplete()"
|
||||
*ngIf="this.mode === 'COMPLETE'">check_circle</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
.path {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
mat-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.edit-area {
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
.command-button {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.path-edit-line {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PathAutoCompleteComponent } from './path-auto-complete.component';
|
||||
|
||||
describe('PathAutoCompleteComponent', () => {
|
||||
let component: PathAutoCompleteComponent;
|
||||
let fixture: ComponentFixture<PathAutoCompleteComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PathAutoCompleteComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PathAutoCompleteComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,74 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {ApiInformationService, IFormatedList} from "@services/api-information.service";
|
||||
import {Server} from "@models/server";
|
||||
|
||||
@Component({
|
||||
selector: 'app-path-auto-complete',
|
||||
templateUrl: './path-auto-complete.component.html',
|
||||
styleUrls: ['./path-auto-complete.component.scss']
|
||||
})
|
||||
export class PathAutoCompleteComponent implements OnInit {
|
||||
|
||||
|
||||
@Output() update = new EventEmitter<string>();
|
||||
@Input() server: Server;
|
||||
path: string[] = [];
|
||||
values: string[] = [];
|
||||
private completeData: IFormatedList[];
|
||||
public mode: 'SELECT' | 'COMPLETE' | undefined;
|
||||
completeField: string;
|
||||
|
||||
constructor(private apiInformationService: ApiInformationService) {
|
||||
|
||||
}
|
||||
|
||||
updatePath(value: string) {
|
||||
this.path.push(value);
|
||||
this.update.emit(`/${this.path.join('/')}`);
|
||||
}
|
||||
|
||||
popPath() {
|
||||
this.path.pop();
|
||||
this.update.emit(`/${this.path.join('/')}`);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
getNext() {
|
||||
this.apiInformationService
|
||||
.getPathNextElement(this.path)
|
||||
.subscribe((next: string[]) => {
|
||||
this.values = next;
|
||||
this.mode = 'SELECT';
|
||||
});
|
||||
}
|
||||
|
||||
removePrevious() {
|
||||
if (this.mode) {
|
||||
return this.mode = undefined;
|
||||
}
|
||||
if (this.path.length > 0) {
|
||||
return this.popPath();
|
||||
}
|
||||
}
|
||||
|
||||
valueChanged(value: string) {
|
||||
if (value.match(this.apiInformationService.bracketIdRegex)) {
|
||||
this.apiInformationService.getListByObjectId(this.server, value)
|
||||
.subscribe((data) => {
|
||||
this.mode = 'COMPLETE';
|
||||
this.completeData = data;
|
||||
});
|
||||
|
||||
} else {
|
||||
this.updatePath(value);
|
||||
this.mode = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
validComplete() {
|
||||
this.updatePath(this.completeField);
|
||||
this.mode = undefined;
|
||||
}
|
||||
}
|
@ -1,15 +1,13 @@
|
||||
<div class="content" *ngIf="isReady; else loading">
|
||||
<div class="default-header">
|
||||
<div class="row">
|
||||
<button class="col" mat-raised-button color="primary" (click)="addPermission()" class="add-button" *ngIf="!newPermissionEdit" >
|
||||
Add Permission
|
||||
</button>
|
||||
<div class="add">
|
||||
<app-add-permission-line
|
||||
[server]="server"
|
||||
(addPermissionEvent)="refresh()"></app-add-permission-line>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="permission-content default-content">
|
||||
<!--<ng-template #dynamic></ng-template>-->
|
||||
<app-add-permission-line [server]="server" (addPermissionEvent)="updateList($event)" *ngIf="newPermissionEdit"></app-add-permission-line>
|
||||
<app-add-permission-line [server]="server" (addPermissionEvent)="updateList($event)"
|
||||
*ngIf="newPermissionEdit"></app-add-permission-line>
|
||||
<div *ngFor="let permission of permissions">
|
||||
<app-permission-add-edit-line
|
||||
[permission]="permission"
|
||||
|
@ -18,3 +18,13 @@
|
||||
top: 0;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
.add {
|
||||
/* display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid;
|
||||
align-items: center;*/
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export interface Permission {
|
||||
path: string;
|
||||
action: PermissionActions;
|
||||
description: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
permission_id: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
permission_id?: string;
|
||||
}
|
||||
|
@ -13,8 +13,10 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {Observable, ReplaySubject} from "rxjs";
|
||||
import {map} from "rxjs/operators";
|
||||
import {map, switchMap} from "rxjs/operators";
|
||||
import {Methods} from "@models/api/permission";
|
||||
import {HttpServer} from "@services/http-server.service";
|
||||
import {Server} from "@models/server";
|
||||
|
||||
export interface IPathDict {
|
||||
methods: ('POST' | 'GET' | 'PUT' | 'DELETE' | 'HEAD' | 'PATH')[];
|
||||
@ -23,37 +25,93 @@ export interface IPathDict {
|
||||
subPaths: string[];
|
||||
}
|
||||
|
||||
export interface IApiObject {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface IQueryObject {
|
||||
id: string;
|
||||
text: string[];
|
||||
}
|
||||
|
||||
export interface IFormatedList {
|
||||
id: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiInformationService {
|
||||
private data: ReplaySubject<IPathDict[]> = new ReplaySubject<IPathDict[]>(1);
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
private allowed = ['projects', 'images', 'templates', 'computes', 'symbols', 'notifications'];
|
||||
private data: ReplaySubject<IPathDict[]> = new ReplaySubject<IPathDict[]>(1);
|
||||
private objs: ReplaySubject<IApiObject[]> = new ReplaySubject<IApiObject[]>(1);
|
||||
public readonly bracketIdRegex = new RegExp("\{(.*?)\}");
|
||||
public readonly finalBracketIdRegex = new RegExp("\{(.*?)\}$");
|
||||
|
||||
constructor(private httpClient: HttpClient,
|
||||
private httpServer: HttpServer) {
|
||||
this.loadLocalInformation();
|
||||
|
||||
this.data.subscribe((data) => {
|
||||
localStorage.setItem('api-definition', JSON.stringify(data));
|
||||
});
|
||||
|
||||
this.objs.subscribe((data) => {
|
||||
localStorage.setItem('api-definition-objs', JSON.stringify(data));
|
||||
});
|
||||
|
||||
|
||||
this.httpClient
|
||||
.get(`https://apiv3.gns3.net/openapi.json`)
|
||||
.subscribe((openapi: any) => {
|
||||
const data = this.apiModelAdapter(openapi);
|
||||
const objs = this.apiObjectModelAdapter(openapi);
|
||||
const data = this.apiPathModelAdapter(openapi);
|
||||
this.data.next(data);
|
||||
this.objs.next(objs);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private apiModelAdapter(openapi: any): IPathDict[] {
|
||||
private apiObjectModelAdapter(openapi: any): IApiObject[] {
|
||||
|
||||
function haveGetMethod(path: string): boolean {
|
||||
const obj = openapi.paths[path];
|
||||
if (obj) {
|
||||
const methods = Object.keys(obj);
|
||||
return methods.includes("get");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function extractId(originalPath: string): IApiObject {
|
||||
const d = originalPath.split('/');
|
||||
|
||||
const name = d.pop();
|
||||
const path = d.join('/');
|
||||
|
||||
return {name, path};
|
||||
}
|
||||
|
||||
const keys = Object.keys(openapi.paths);
|
||||
return keys
|
||||
.filter((path: string) => path.match(this.finalBracketIdRegex))
|
||||
.filter(haveGetMethod)
|
||||
.map(extractId)
|
||||
.filter((object) => haveGetMethod(object.path));
|
||||
}
|
||||
|
||||
private apiPathModelAdapter(openapi: any): IPathDict[] {
|
||||
const keys = Object.keys(openapi.paths);
|
||||
return keys
|
||||
.map(path => {
|
||||
const subPaths = path.split('/').filter(elem => !(elem === '' || elem === 'v3'));
|
||||
return {originalPath: path, path: subPaths.join('/'), subPaths};
|
||||
})
|
||||
.filter(d => this.allowed.includes(d.subPaths[0]))
|
||||
.map(path => {
|
||||
//FIXME
|
||||
// @ts-ignore
|
||||
@ -69,7 +127,6 @@ export class ApiInformationService {
|
||||
.pipe(
|
||||
map((data: IPathDict[]) => {
|
||||
const availableMethods = new Set<string>();
|
||||
|
||||
data.forEach((p: IPathDict) => {
|
||||
p.methods.forEach(method => availableMethods.add(method));
|
||||
});
|
||||
@ -83,24 +140,24 @@ export class ApiInformationService {
|
||||
.asObservable()
|
||||
.pipe(
|
||||
map((data) => {
|
||||
const splinted = path
|
||||
.split('/')
|
||||
.filter(elem => !(elem === '' || elem === 'v3'));
|
||||
|
||||
const splinted = path.split('/').filter(elem => !(elem === '' || elem === 'v3'));
|
||||
let remains = data;
|
||||
splinted.forEach((value, index) => {
|
||||
if (value === '*') {
|
||||
return remains;
|
||||
return;
|
||||
}
|
||||
remains = remains.filter((val => {
|
||||
if (!val.subPaths[index]) {
|
||||
return false;
|
||||
}
|
||||
if (val.subPaths[index].includes('{')) {
|
||||
return true;
|
||||
}
|
||||
return val.subPaths[index] === value;
|
||||
}));
|
||||
});
|
||||
let matchUrl = remains.filter(val => val.subPaths[index]?.includes(value));
|
||||
|
||||
if (matchUrl.length === 0) {
|
||||
matchUrl = remains.filter(val => val.subPaths[index]?.match(this.bracketIdRegex));
|
||||
}
|
||||
|
||||
remains = matchUrl;
|
||||
|
||||
});
|
||||
return remains;
|
||||
})
|
||||
);
|
||||
@ -111,5 +168,63 @@ export class ApiInformationService {
|
||||
if (data) {
|
||||
this.data.next(data);
|
||||
}
|
||||
const obj = JSON.parse(localStorage.getItem('api-definition-objs'));
|
||||
if (obj) {
|
||||
this.objs.next(obj);
|
||||
}
|
||||
}
|
||||
|
||||
getPathNextElement(path: string[]): Observable<string[]> {
|
||||
|
||||
return this.getPath(path.join('/'))
|
||||
.pipe(map((paths: IPathDict[]) => {
|
||||
const set = new Set<string>();
|
||||
paths.forEach((p) => {
|
||||
if (p.subPaths[path.length]) {
|
||||
set.add(p.subPaths[path.length]);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(set);
|
||||
}));
|
||||
}
|
||||
|
||||
getListByObjectId(server: Server, value: string) {
|
||||
|
||||
function findElement(data: IApiObject[]): IApiObject {
|
||||
const elem = data.find(d => d.name === value);
|
||||
if (!elem) {
|
||||
throw new Error('entry not found');
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
return this.objs.pipe(
|
||||
map(findElement),
|
||||
switchMap(elem => {
|
||||
const url = `${server.protocol}//${server.host}:${server.port}${elem.path}`;
|
||||
return this.httpClient.get<any[]>(url, {headers: {Authorization: `Bearer ${server.authToken}`}});
|
||||
}
|
||||
),
|
||||
map(response => {
|
||||
|
||||
if (response.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const keys = Object.keys(response[0]);
|
||||
const idKey = keys.find(k => k.match(/_id$|filename/));
|
||||
const nameKey = keys.find(k => k.match(/name/));
|
||||
|
||||
return response.map(o => {
|
||||
return {
|
||||
id: o[idKey],
|
||||
name: o[nameKey]
|
||||
};
|
||||
});
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user