This repository has been archived by the owner on Jan 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add backend support for API keys * Add last_used field to API keys * Add base api keys page * Add basic api key entity framework (untested) * Add a basic api keys list (untested, need to wire in properties/columns + actions) * Fix entity type related issues * Add basic way to create api key * Wire in delete to list * Improve 'no api keys' ux * Final tidy up * Other fixes * Fix unit tests * Add 'Last Used' column to API keys list * Don't flash up 'no entries' when we haven't loaded api keys yet * Fix last used sorting - takes into account timezone - use cacheing to cater for often called sort * Fix unit test after changes in master * Fix after moment change * Remove now unrequired sorting of api key last used date via moment Co-authored-by: Ivan Kapelyukhin <ikapelyukhin@suse.com>
- Loading branch information
1 parent
f6f2d9f
commit b560761
Showing
26 changed files
with
791 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
.../packages/core/src/features/api-keys/add-api-key-dialog/add-api-key-dialog.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<div class="key-dialog__loading-wrapper"> | ||
<mat-progress-bar class="key-dialog__loading" | ||
[color]="(hasErrored$ | async) && !(isBusy$ | async) ? 'warn' : 'primary'" | ||
[mode]="(isBusy$ | async) ? 'indeterminate' : 'solid'"> | ||
</mat-progress-bar> | ||
</div> | ||
<div class="key-dialog"> | ||
<div class="key-dialog__title"> | ||
<h2 mat-dialog-title> | ||
Create an API Key | ||
</h2> | ||
</div> | ||
<div> | ||
</div> | ||
<div [formGroup]="formGroup"> | ||
<mat-form-field> | ||
<input matInput placeholder="Description" formControlName="comment" required> | ||
</mat-form-field> | ||
</div> | ||
|
||
<app-dialog-error message="{{hasErrored$ | async}}" [show]="(hasErrored$ | async) && !(isBusy$ | async)"> | ||
</app-dialog-error> | ||
<mat-dialog-actions class="key-dialog__actions"> | ||
<button [mat-dialog-close]="true" mat-button color="warn" [disabled]="(isBusy$ | async)">Cancel</button> | ||
<button (click)="submit()" [disabled]="(isBusy$ | async) || !formGroup.valid" mat-button | ||
color="primary">Create</button> | ||
</mat-dialog-actions> | ||
</div> |
28 changes: 28 additions & 0 deletions
28
.../packages/core/src/features/api-keys/add-api-key-dialog/add-api-key-dialog.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
.key-dialog { | ||
&__loading { | ||
left: 0; | ||
position: absolute; | ||
right: 0; | ||
top: 0; | ||
&-wrapper { | ||
position: relative; | ||
margin: 0 -24px; | ||
transform: translateY(-24px); | ||
} | ||
} | ||
|
||
&__title { | ||
display: flex; | ||
h2 { | ||
flex: 1; | ||
} | ||
} | ||
|
||
&__actions { | ||
justify-content: flex-end; | ||
} | ||
|
||
mat-form-field { | ||
width: 100%; | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...ckages/core/src/features/api-keys/add-api-key-dialog/add-api-key-dialog.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { MatDialogRef } from '@angular/material/dialog'; | ||
|
||
import { BaseTestModules } from '../../../../test-framework/core-test.helper'; | ||
import { AddApiKeyDialogComponent } from './add-api-key-dialog.component'; | ||
|
||
describe('AddApiKeyDialogComponent', () => { | ||
let component: AddApiKeyDialogComponent; | ||
let fixture: ComponentFixture<AddApiKeyDialogComponent>; | ||
|
||
const mockDialogRef = { | ||
close: () => { } | ||
}; | ||
|
||
beforeEach(async(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ | ||
...BaseTestModules, | ||
], | ||
declarations: [AddApiKeyDialogComponent], | ||
providers: [ | ||
{ | ||
provide: MatDialogRef, | ||
useValue: mockDialogRef | ||
} | ||
] | ||
}) | ||
.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(AddApiKeyDialogComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
66 changes: 66 additions & 0 deletions
66
...nd/packages/core/src/features/api-keys/add-api-key-dialog/add-api-key-dialog.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Component, OnDestroy } from '@angular/core'; | ||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; | ||
import { MatDialogRef } from '@angular/material/dialog'; | ||
import { BehaviorSubject, Subscription } from 'rxjs'; | ||
import { filter, first, map, pairwise, tap } from 'rxjs/operators'; | ||
|
||
import { ApiKey } from '../../../../../store/src/apiKey.types'; | ||
import { entityCatalog } from '../../../../../store/src/entity-catalog/entity-catalog'; | ||
import { RequestInfoState } from '../../../../../store/src/reducers/api-request-reducer/types'; | ||
import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-catalog'; | ||
import { NormalizedResponse } from '../../../../../store/src/types/api.types'; | ||
import { safeUnsubscribe } from '../../../core/utils.service'; | ||
|
||
@Component({ | ||
selector: 'app-add-api-key-dialog', | ||
templateUrl: './add-api-key-dialog.component.html', | ||
styleUrls: ['./add-api-key-dialog.component.scss'] | ||
}) | ||
export class AddApiKeyDialogComponent implements OnDestroy { | ||
|
||
private hasErrored = new BehaviorSubject(null); | ||
public hasErrored$ = this.hasErrored.asObservable(); | ||
private isBusy = new BehaviorSubject(false) | ||
public isBusy$ = this.isBusy.asObservable(); | ||
|
||
private sub: Subscription; | ||
|
||
public formGroup: FormGroup; | ||
|
||
constructor( | ||
private fb: FormBuilder, | ||
public dialogRef: MatDialogRef<ApiKey>, | ||
) { | ||
this.formGroup = this.fb.group({ | ||
comment: ['', Validators.required], | ||
}); | ||
} | ||
|
||
ngOnDestroy(): void { | ||
safeUnsubscribe(this.sub); | ||
} | ||
|
||
submit() { | ||
this.sub = stratosEntityCatalog.apiKey.api.create<RequestInfoState>(this.formGroup.controls.comment.value).pipe( | ||
tap(() => { | ||
this.isBusy.next(true); | ||
this.hasErrored.next(null); | ||
}), | ||
pairwise(), | ||
filter(([oldR, newR]) => oldR.creating && !newR.creating), | ||
map(([, newR]) => newR), | ||
tap(state => { | ||
if (state.error) { | ||
this.hasErrored.next(`Failed to create key: ${state.message}`); | ||
this.isBusy.next(false); | ||
} else { | ||
const response: NormalizedResponse<ApiKey> = state.response; | ||
const entityKey = entityCatalog.getEntityKey(stratosEntityCatalog.apiKey.actions.create('')); | ||
this.dialogRef.close(response.entities[entityKey][response.result[0]]) | ||
} | ||
}), | ||
first() | ||
).subscribe() | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
src/frontend/packages/core/src/features/api-keys/api-keys-page/api-keys-page.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<app-page-header> | ||
<h1>API Keys</h1> | ||
<div class="page-header-right"> | ||
<button id="stratos-api-key" mat-icon-button (click)="addApiKey()" matTooltip="Create API Key"> | ||
<mat-icon>add</mat-icon> | ||
</button> | ||
</div> | ||
</app-page-header> | ||
|
||
<div class="keys-page__new" *ngIf="keyDetails$ | async as keyDetails"> | ||
|
||
<mat-card class="autoscaler-credential__card"> | ||
<mat-card-header> | ||
<mat-icon>vpn_key</mat-icon>New API Key | ||
</mat-card-header> | ||
<mat-card-content> | ||
<p>Your API Key has been successfully created. Use the following information to connect to Stratos.</p> | ||
<p><i>Please safely record these details, there is no later way to view them</i></p> | ||
<ul> | ||
<li>Secret: {{keyDetails.secret}}</li> | ||
</ul> | ||
<button (click)="clearKeyDetails()" mat-button color="warn">Close</button> | ||
</mat-card-content> | ||
</mat-card> | ||
</div> | ||
|
||
<app-list *ngIf="(hasKeys$ | async) === true"></app-list> | ||
<app-no-content-message *ngIf="(hasKeys$ | async) === false" icon="vpn_key" [firstLine]="'You have no API keys'" | ||
[secondLine]="{ | ||
text: '' | ||
}" [toolbarLink]="{ | ||
text: 'Create an API key' | ||
}" toolbarAlign="stratos-api-key"></app-no-content-message> |
15 changes: 15 additions & 0 deletions
15
src/frontend/packages/core/src/features/api-keys/api-keys-page/api-keys-page.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
.keys-page { | ||
&__new { | ||
mat-card { | ||
margin-bottom: 24px; | ||
mat-card-header { | ||
align-items: center; | ||
display: flex; | ||
margin-bottom: 15px; | ||
mat-icon { | ||
margin-right: 5px; | ||
} | ||
} | ||
} | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...rontend/packages/core/src/features/api-keys/api-keys-page/api-keys-page.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { MatDialogRef } from '@angular/material/dialog'; | ||
|
||
import { BaseTestModules } from '../../../../test-framework/core-test.helper'; | ||
import { TabNavService } from '../../../tab-nav.service'; | ||
import { ApiKeysPageComponent } from './api-keys-page.component'; | ||
|
||
describe('ApiKeysPageComponent', () => { | ||
let component: ApiKeysPageComponent; | ||
let fixture: ComponentFixture<ApiKeysPageComponent>; | ||
|
||
const mockDialogRef = { | ||
close: () => { } | ||
}; | ||
|
||
beforeEach(async(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ | ||
...BaseTestModules, | ||
], | ||
declarations: [ApiKeysPageComponent], | ||
providers: [ | ||
{ | ||
provide: MatDialogRef, | ||
useValue: mockDialogRef | ||
}, | ||
TabNavService | ||
] | ||
}) | ||
.compileComponents(); | ||
})); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(ApiKeysPageComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
59 changes: 59 additions & 0 deletions
59
src/frontend/packages/core/src/features/api-keys/api-keys-page/api-keys-page.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { Component } from '@angular/core'; | ||
import { MatDialog } from '@angular/material/dialog'; | ||
import { Observable, Subject } from 'rxjs'; | ||
import { first, map, startWith } from 'rxjs/operators'; | ||
|
||
import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-catalog'; | ||
import { ApiKeyListConfigService } from '../../../shared/components/list/list-types/apiKeys/apiKey-list-config.service'; | ||
import { ListConfig } from '../../../shared/components/list/list.component.types'; | ||
import { AddApiKeyDialogComponent } from '../add-api-key-dialog/add-api-key-dialog.component'; | ||
|
||
@Component({ | ||
selector: 'app-api-keys-page', | ||
templateUrl: './api-keys-page.component.html', | ||
styleUrls: ['./api-keys-page.component.scss'], | ||
providers: [{ | ||
provide: ListConfig, | ||
useClass: ApiKeyListConfigService, | ||
}] | ||
}) | ||
export class ApiKeysPageComponent { | ||
|
||
public keyDetails = new Subject<string>(); | ||
public keyDetails$ = this.keyDetails.asObservable(); | ||
public hasKeys$: Observable<Boolean>; | ||
|
||
constructor( | ||
private dialog: MatDialog, | ||
) { | ||
this.hasKeys$ = stratosEntityCatalog.apiKey.store.getPaginationService().entities$.pipe( | ||
map(entities => entities && !!entities.length), | ||
startWith(null), | ||
) | ||
} | ||
|
||
addApiKey() { | ||
this.showDialog().pipe(first()).subscribe(key => { | ||
this.keyDetails.next(key); | ||
}) | ||
} | ||
|
||
clearKeyDetails() { | ||
this.keyDetails.next(); | ||
} | ||
|
||
private showDialog(): Observable<string> { | ||
return this.dialog.open(AddApiKeyDialogComponent, { | ||
disableClose: true, | ||
}).afterClosed().pipe( | ||
map(newApiKey => { | ||
if (newApiKey && newApiKey.guid) { | ||
stratosEntityCatalog.apiKey.api.getMultiple(); | ||
return newApiKey; | ||
} | ||
return null; | ||
}) | ||
); | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
src/frontend/packages/core/src/features/api-keys/api-keys.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { NgModule } from '@angular/core'; | ||
|
||
import { CoreModule } from '../../core/core.module'; | ||
import { SharedModule } from '../../shared/shared.module'; | ||
import { ApiKeysPageComponent } from './api-keys-page/api-keys-page.component'; | ||
import { ApiKeysRoutingModule } from './api-keys.routing'; | ||
import { AddApiKeyDialogComponent } from './add-api-key-dialog/add-api-key-dialog.component'; | ||
|
||
|
||
@NgModule({ | ||
imports: [ | ||
CoreModule, | ||
SharedModule, | ||
ApiKeysRoutingModule, | ||
], | ||
declarations: [ | ||
ApiKeysPageComponent, | ||
AddApiKeyDialogComponent | ||
] | ||
}) | ||
export class ApiKeysModule { } | ||
|
18 changes: 18 additions & 0 deletions
18
src/frontend/packages/core/src/features/api-keys/api-keys.routing.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { NgModule } from '@angular/core'; | ||
import { RouterModule, Routes } from '@angular/router'; | ||
|
||
import { ApiKeysPageComponent } from './api-keys-page/api-keys-page.component'; | ||
|
||
const apiKeys: Routes = [ | ||
{ | ||
path: '', | ||
component: ApiKeysPageComponent | ||
}, | ||
]; | ||
|
||
@NgModule({ | ||
imports: [ | ||
RouterModule.forChild(apiKeys), | ||
] | ||
}) | ||
export class ApiKeysRoutingModule { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.