Skip to content

Commit fc418c7

Browse files
authored
Merge pull request #62 from OS2iot/feature/1220_api-key
Feature/1220 api key
2 parents 883aa87 + 4ee49a2 commit fc418c7

22 files changed

+590
-14
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules\\typescript\\lib"
3+
}

src/app/admin/admin-routing.module.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { UserDetailComponent } from './users/user-detail/user-detail.component';
1212
import { UserEditComponent } from './users/user-edit/user-edit.component';
1313
import { UserListComponent } from './users/user-list/user-list.component';
1414
import { UsersComponent } from './users/users.component';
15+
import { ApiKeyComponent } from './api-key/api-key.component';
16+
import { ApiKeyListComponent } from './api-key/api-key-list/api-key-list.component';
17+
import { ApiKeyEditComponent } from './api-key/api-key-edit/api-key-edit.component';
1518

1619

1720
const adminRoutes: Routes = [
@@ -44,6 +47,18 @@ const adminRoutes: Routes = [
4447
},
4548
],
4649
},
50+
{
51+
path: 'api-key',
52+
component: ApiKeyComponent,
53+
children: [
54+
{ path: '', component: ApiKeyListComponent },
55+
{ path: 'new-api-key', component: ApiKeyEditComponent },
56+
{
57+
path: ':api-key-id/edit-api-key',
58+
component: ApiKeyEditComponent,
59+
},
60+
],
61+
},
4762

4863

4964
];

src/app/admin/admin.module.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ import { UsersComponent } from './users/users.component';
2828
import { MatSelectModule } from '@angular/material/select';
2929
import { MatFormFieldModule } from '@angular/material/form-field';
3030
import { MatSelectSearchModule } from '@shared/components/mat-select-search/mat-select-search.module';
31+
import { ApiKeyComponent } from './api-key/api-key.component';
32+
import { ApiKeyListComponent } from './api-key/api-key-list/api-key-list.component';
33+
import { ApiKeyTableComponent } from './api-key/api-key-list/api-key-table/api-key-table.component';
34+
import { ApiKeyEditComponent } from './api-key/api-key-edit/api-key-edit.component';
3135

3236
@NgModule({
3337
declarations: [
@@ -46,6 +50,10 @@ import { MatSelectSearchModule } from '@shared/components/mat-select-search/mat-
4650
OrganisationDetailComponent,
4751
OrganisationEditComponent,
4852
OrganisationListComponent,
53+
ApiKeyComponent,
54+
ApiKeyListComponent,
55+
ApiKeyTableComponent,
56+
ApiKeyEditComponent,
4957
],
5058
imports: [
5159
AdminRoutingModule,
@@ -79,6 +87,10 @@ import { MatSelectSearchModule } from '@shared/components/mat-select-search/mat-
7987
OrganisationDetailComponent,
8088
OrganisationEditComponent,
8189
OrganisationListComponent,
90+
ApiKeyComponent,
91+
ApiKeyListComponent,
92+
ApiKeyTableComponent,
93+
ApiKeyEditComponent,
8294
],
8395
})
8496
export class AdminModule {}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<app-form-header [backButton]="backButton" [title]="title"></app-form-header>
2+
3+
<form (ngSubmit)="onSubmit()" class="os2-form p-3 mt-4">
4+
<div *ngIf="errorMessages" class="error-messages p-3">
5+
<ul class="mb-0">
6+
<li *ngFor="let error of errorMessages">
7+
{{ error | translate }}
8+
</li>
9+
</ul>
10+
</div>
11+
12+
<div class="row mb-5">
13+
<div class="form-group mt-3 col-12">
14+
<label class="form-label" for="name">{{
15+
'API-KEY.EDIT.NAME' | translate
16+
}}</label
17+
>*
18+
<input
19+
type="text"
20+
class="form-control"
21+
id="name"
22+
name="name"
23+
[placeholder]="'API-KEY.EDIT.NAME-PLACEHOLDER' | translate"
24+
maxlength="50"
25+
required
26+
[(ngModel)]="apiKeyRequest.name"
27+
[ngClass]="{
28+
'is-invalid': formFailedSubmit && errorFields.includes('name'),
29+
'is-valid': formFailedSubmit && !errorFields.includes('name')
30+
}"
31+
/>
32+
</div>
33+
</div>
34+
35+
<div class="row mb-5">
36+
<div class="form-group mt-3 col-12">
37+
<label class="form-label" for="permissions">{{
38+
'QUESTION.PERMISSION.SELECT-PERMISSION' | translate
39+
}}</label
40+
>*
41+
<mat-select
42+
class="form-control"
43+
name="permissions"
44+
[compareWith]="compare"
45+
[(ngModel)]="apiKeyRequest.permissions"
46+
[multiple]="true"
47+
>
48+
<mat-option
49+
*ngFor="let permission of permissions"
50+
[value]="permission.id"
51+
>
52+
{{ permission.name }}
53+
</mat-option>
54+
</mat-select>
55+
</div>
56+
</div>
57+
58+
<div class="form-group mt-5">
59+
<button (click)="routeBack()" class="btn btn-secondary" type="button">
60+
{{ 'GEN.CANCEL' | translate }}
61+
</button>
62+
<button class="btn btn-primary ml-2" type="submit">
63+
{{ 'GEN.SAVE' | translate }}
64+
</button>
65+
</div>
66+
</form>

src/app/admin/api-key/api-key-edit/api-key-edit.component.scss

Whitespace-only changes.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { Location } from '@angular/common';
2+
import { HttpErrorResponse } from '@angular/common/http';
3+
import { Component, OnInit } from '@angular/core';
4+
import { ActivatedRoute } from '@angular/router';
5+
import { PermissionResponse } from '@app/admin/permission/permission.model';
6+
import { PermissionService } from '@app/admin/permission/permission.service';
7+
import { TranslateService } from '@ngx-translate/core';
8+
import { ErrorMessageService } from '@shared/error-message.service';
9+
import { BackButton } from '@shared/models/back-button.model';
10+
import { SharedVariableService } from '@shared/shared-variable/shared-variable.service';
11+
import { ApiKeyRequest } from '../api-key.model';
12+
import { ApiKeyService } from '../api-key.service';
13+
14+
@Component({
15+
selector: 'app-api-key-edit',
16+
templateUrl: './api-key-edit.component.html',
17+
styleUrls: ['./api-key-edit.component.scss'],
18+
})
19+
export class ApiKeyEditComponent implements OnInit {
20+
apiKeyRequest = new ApiKeyRequest();
21+
public backButton: BackButton = {
22+
label: '',
23+
routerLink: ['admin', 'api-key'],
24+
};
25+
public title = '';
26+
public submitButton = '';
27+
public errorMessage: string;
28+
public errorMessages: string[];
29+
public errorFields: string[];
30+
public formFailedSubmit = false;
31+
public permissions: PermissionResponse[] = [];
32+
private organizationId: number;
33+
34+
constructor(
35+
private translate: TranslateService,
36+
private route: ActivatedRoute,
37+
private location: Location,
38+
private apiKeyService: ApiKeyService,
39+
private permissionService: PermissionService,
40+
private errorMessageService: ErrorMessageService,
41+
private sharedVariableService: SharedVariableService
42+
) {
43+
translate.use('da');
44+
}
45+
46+
ngOnInit(): void {
47+
this.getPermissions();
48+
this.translate.use('da');
49+
this.translate
50+
.get(['NAV.API-KEY', 'FORM.EDIT-API-KEY', 'API-KEY.EDIT.SAVE'])
51+
.subscribe((translations) => {
52+
this.backButton.label = translations['NAV.API-KEY'];
53+
this.title = translations['FORM.EDIT-API-KEY'];
54+
this.submitButton = translations['API-KEY.EDIT.SAVE'];
55+
});
56+
57+
this.organizationId = this.sharedVariableService.getSelectedOrganisationId();
58+
}
59+
60+
private getPermissions() {
61+
this.permissionService
62+
.getPermissions(
63+
undefined,
64+
undefined,
65+
undefined,
66+
undefined,
67+
undefined,
68+
this.organizationId
69+
)
70+
.subscribe(
71+
(permissions) => {
72+
this.permissions = permissions.data.filter(
73+
(x) => x.organization?.id === this.organizationId
74+
);
75+
},
76+
(error: HttpErrorResponse) => {
77+
this.showError(error);
78+
}
79+
);
80+
}
81+
82+
onSubmit(): void {
83+
this.create();
84+
}
85+
86+
private create(): void {
87+
this.apiKeyService.create(this.apiKeyRequest).subscribe(
88+
() => this.routeBack(),
89+
(err) => this.showError(err)
90+
);
91+
}
92+
93+
public compare(o1: any, o2: any): boolean {
94+
return o1 === o2;
95+
}
96+
97+
private showError(err: HttpErrorResponse) {
98+
const result = this.errorMessageService.handleErrorMessageWithFields(err);
99+
this.errorFields = result.errorFields;
100+
this.errorMessages = result.errorMessages;
101+
}
102+
103+
routeBack(): void {
104+
this.location.back();
105+
}
106+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<app-top-bar
2+
[title]="'NAV.API-KEY' | translate"
3+
[ctaLabel]="'API-KEY.CREATE-NEW-API-KEY' | translate"
4+
[ctaRouterLink]="'new-api-key'"
5+
>
6+
</app-top-bar>
7+
<div class="container-fluid">
8+
<div class="row">
9+
<div class="col-12">
10+
<div class="jumbotron--table">
11+
<app-api-key-table
12+
[organisationId]="organisationId"
13+
></app-api-key-table>
14+
</div>
15+
</div>
16+
</div>
17+
</div>

src/app/admin/api-key/api-key-list/api-key-list.component.scss

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Component, Input, OnInit } from '@angular/core';
2+
import { Title } from '@angular/platform-browser';
3+
import { TranslateService } from '@ngx-translate/core';
4+
import { SharedVariableService } from '@shared/shared-variable/shared-variable.service';
5+
6+
@Component({
7+
selector: 'app-api-key-list',
8+
templateUrl: './api-key-list.component.html',
9+
styleUrls: ['./api-key-list.component.scss'],
10+
})
11+
export class ApiKeyListComponent implements OnInit {
12+
@Input() organisationId: number;
13+
14+
constructor(
15+
public translate: TranslateService,
16+
private titleService: Title,
17+
private globalService: SharedVariableService
18+
) {
19+
translate.use('da');
20+
}
21+
22+
ngOnInit(): void {
23+
this.translate.get(['TITLE.API-KEY']).subscribe((translations) => {
24+
this.titleService.setTitle(translations['TITLE.API-KEY']);
25+
});
26+
this.organisationId = this.globalService.getSelectedOrganisationId();
27+
}
28+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<div class="mat-elevation-z8">
2+
<div class="loading-shade" *ngIf="isLoadingResults">
3+
<mat-spinner *ngIf="isLoadingResults"></mat-spinner>
4+
</div>
5+
<table
6+
mat-table
7+
[dataSource]="data"
8+
matSort
9+
matSortActive="name"
10+
matSortDirection="asc"
11+
matSortDisableClear
12+
>
13+
<!-- Name Column -->
14+
<ng-container matColumnDef="name">
15+
<th mat-header-cell *matHeaderCellDef mat-sort-header>
16+
{{ 'API-KEY.NAME' | translate }}
17+
</th>
18+
<td mat-cell *matCellDef="let element">
19+
{{ element.name }}
20+
</td>
21+
</ng-container>
22+
23+
<!-- User Groups Column -->
24+
<ng-container matColumnDef="permissions">
25+
<th mat-header-cell *matHeaderCellDef>
26+
{{ 'API-KEY.PERMISSIONS' | translate }}
27+
</th>
28+
<td mat-cell *matCellDef="let element">
29+
<ng-container *ngIf="element.permissions; else noUsers">
30+
<ng-container *ngFor="let pm of element.permissions">
31+
<span>{{ pm.name }}</span>
32+
<br />
33+
</ng-container>
34+
</ng-container>
35+
<ng-template #noUsers>{{ 'NoUsersAdded' | translate }}</ng-template>
36+
</td>
37+
</ng-container>
38+
39+
<!-- Key Column -->
40+
<ng-container matColumnDef="key">
41+
<th mat-header-cell *matHeaderCellDef>
42+
{{ 'API-KEY.KEY' | translate }}
43+
</th>
44+
<td mat-cell *matCellDef="let element">
45+
{{ element.key }}
46+
</td>
47+
</ng-container>
48+
49+
<!-- Menu Column -->
50+
<ng-container matColumnDef="menu">
51+
<th mat-header-cell *matHeaderCellDef></th>
52+
<td mat-cell *matCellDef="let element">
53+
<a
54+
*ngIf="canAccess(element)"
55+
(click)="deleteApiKey(element.id)"
56+
[routerLink]=""
57+
>{{ 'API-KEY.TABLE-ROW.DELETE' | translate }}
58+
</a>
59+
</td>
60+
</ng-container>
61+
62+
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
63+
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
64+
</table>
65+
<mat-paginator
66+
[pageSizeOptions]="[5, 10, 20]"
67+
[pageSize]="pageSize"
68+
[length]="resultsLength"
69+
showFirstLastButtons
70+
>
71+
</mat-paginator>
72+
</div>

0 commit comments

Comments
 (0)