diff --git a/src/app/core/components/user-services/user-services.component.html b/src/app/core/components/user-services/user-services.component.html index c160b33875b4f7689b9ef24a69cffdcd0f88ffd4..cf125925f5c6ea1a9eedab1f3aeb4186dba79b76 100644 --- a/src/app/core/components/user-services/user-services.component.html +++ b/src/app/core/components/user-services/user-services.component.html @@ -25,4 +25,49 @@ </tr> </tbody> </table> + + <div> + <div class="requestable-services-table-wrapper"> + <table class="table"> + <thead> + <tr> + <th></th> + <th i18n="@@userService.name">Name</th> + <th>Service</th> + <th i18n="@@userService.add">Add</th> + </tr> + </thead> + <tbody> + <ng-container *ngFor="let service of requestableServices; let index = index"> + <tr> + <td (click)="toogleRow(index)"> + <i class="fas fa-angle-right" *ngIf="!isRowOpened(index); else angleDownTemplate"></i> + <ng-template #angleDownTemplate> + <i class="fas fa-angle-down"></i> + </ng-template> + </td> + <td>{{ service.name }}</td> + <td>all</td> + <td><input type="checkbox" (click)="toogleService(service.id)" [checked]="allModeAreSelected(service.id)"></td> + </tr> + <ng-container *ngIf="isRowOpened(index)"> + <tr *ngFor="let mode of service.modes;"> + <td></td> + <td></td> + <td> + {{ mode.name }} + </td> + <td> + <input type="checkbox" [(ngModel)]="mode.selected"> + </td> + </tr> + </ng-container> + </ng-container> + </tbody> + </table> + </div> + </div> + <button type="button" class="button button-gl" [disabled]="requestedServices.length <= 0" (click)="requestAccess()" i18n="@@userService.sendRequest"> + Send my request + </button> </section> \ No newline at end of file diff --git a/src/app/core/components/user-services/user-services.component.scss b/src/app/core/components/user-services/user-services.component.scss index fc3be280571f09b972f353297f005f08775e72b4..6d9bd22910bef69ca2a47871f78d55282245cdca 100644 --- a/src/app/core/components/user-services/user-services.component.scss +++ b/src/app/core/components/user-services/user-services.component.scss @@ -1,3 +1,13 @@ .lock-open-icon { margin-right: 0.5rem; +} + +i { + cursor: pointer; +} + +.requestable-services-table-wrapper { + max-height: 50vh; + overflow-y: scroll; + display: inline-block; } \ No newline at end of file diff --git a/src/app/core/components/user-services/user-services.component.ts b/src/app/core/components/user-services/user-services.component.ts index 264cc09d7863183350da8151e425fc8b6140d0c9..7a8941b47c37827ad9e9a33be35aa8365bdd872a 100644 --- a/src/app/core/components/user-services/user-services.component.ts +++ b/src/app/core/components/user-services/user-services.component.ts @@ -1,7 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { AuthService } from '../../services'; -import { UserService } from '../../models/auth.model'; +import { AuthService, NotificationService } from '../../services'; import { statuses } from '../../../../i18n/user-services/user-services'; +import { IRequestedAccessService, IRestrictedAccessService, IMode } from '../../models/auth.model'; +import { forkJoin } from 'rxjs'; +import { Notification } from '../../models'; +import { messages } from '../../../../i18n/notification-messages/notification-messages'; @Component({ selector: 'app-user-services', @@ -10,20 +13,148 @@ import { statuses } from '../../../../i18n/user-services/user-services'; }) export class UserServicesComponent implements OnInit { - userServices: UserService[] = []; + userServices: IRequestedAccessService[] = []; + restrictedAccessServices: IRestrictedAccessService[] = []; + modes: IMode[] = []; statusesTrad = statuses; + openedRow: number[] = []; + + requestableServices = []; + constructor( private _authService: AuthService, + private _notificationService: NotificationService, ) { } ngOnInit() { - this.getUserServices(); + forkJoin([ + this._authService.getUserServices(), + this._authService.getModes(), + this._authService.getRestrictedAccessService(), + ]).subscribe( + (results) => { + this.userServices = results[0]; + this.modes = results[1]; + this.restrictedAccessServices = results[2]; + + this.initRequestableServices(); + }, + (err) => { + console.log('Something went wrong', err); + }, + ); + } + + initRequestableServices() { + this.requestableServices = []; + // Iterate over the access restricted services + this.restrictedAccessServices.forEach((service) => { + const temp = { id: service.dataset_id, name: service.dataset_name, modes: [] }; + + this.modes.forEach((mode) => { + // Add mode if not already in the user available dataset + // meaning if the user didn't already have requested the access for that dataset and that mode) + if ( + this.userServices.findIndex( + us => us.dataset_id === service.dataset_id && us.ressource === mode.name, + ) === -1) { + temp.modes.push({ id: mode.id, name: mode.name, selected: false }); + } + }); + + this.requestableServices.push(temp); + }); + } + + // Returns 1 if number is in array, -1 else + isRowOpened(id: number): boolean { + return this.openedRow.includes(id); + } + + toogleRow(id: number) { + if (this.isRowOpened(id)) { + this.removeRow(id); + } else { + this.addRow(id); + } + } + + addRow(id: number) { + this.openedRow.push(id); + } + + removeRow(id) { + const index = this.openedRow.findIndex(row => row === id); + if (index !== -1) { + this.openedRow.splice(index, 1); + } + } + + toogleService(serviceId) { + const service = this.requestableServices.find(e => e.id === serviceId); + if (service) { + let bool = false; + const unselectedMode = service.modes.find(e => e.selected === false); + + if (unselectedMode) { + bool = true; + } + + service.modes.forEach((mode) => { + mode.selected = bool; + }); + } + } + + allModeAreSelected(serviceId: number) { + let res = true; + + const service = this.requestableServices.find(e => e.id === serviceId); + if (service) { + const unselectedMode = service.modes.find(e => e.selected === false); + + if (unselectedMode) { + res = false; + } + } + + return res; + } + + get requestedServices() { + return this.requestableServices.filter((e) => { + return e.modes.findIndex(m => m.selected === true) !== -1 ? true : false; + }).map((e) => { + const modesIdArray = e.modes.filter(m => m.selected === true).map((m) => { return { id: m.id, name: m.name }; }); + return { id: e.id, name: e.name, modes: modesIdArray }; + }); } - getUserServices() { - this._authService.getUserServices().subscribe((userServices) => { - this.userServices = userServices; + requestAccess() { + this.requestedServices.forEach((e) => { + this._authService.requestAccessToService({ id: e.id, modes: e.modes.map(e => e.id) }).subscribe( + (res) => { + this._notificationService.notify(new Notification({ + type: 'success', + message: `${messages.userServices.addSuccess} ${e.name} (${e.modes.map(e => e.name)})`, + })); + this._authService.getUserServices().subscribe( + (res) => { + this.userServices = res; + this.initRequestableServices(); + }, + (err) => { + }, + ); + }, + (err) => { + console.log(err); + this._notificationService.notify(new Notification({ + type: 'error', + message: `${messages.userServices.addFailed} "${e.name}" (${e.modes.map(e => e.name)})`, + })); + }); }); } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 488fe2111c2d8e7bd8ec8e4d5b7696b434361e7c..79f3eefe1d08207fc9b0fb62f68fcb68a38d7e14 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -8,7 +8,7 @@ import { ErrorsHandler } from './handlers/errors-handler'; import { HttpErrorResponseInterceptor } from './interceptors/http-error-response-interceptor'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { CoreServices } from './services'; -import { ReactiveFormsModule } from '@angular/forms'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; import { AuthInterceptor } from './interceptors/auth-interceptor'; import { GeosourceModule } from '../geosource/geosource.module'; import { CoreGuards } from './guards'; @@ -19,6 +19,7 @@ import { CoreGuards } from './guards'; CoreRoutingModule, SharedModule, ReactiveFormsModule, + FormsModule, GeosourceModule.forRoot(), ], declarations: [CoreComponents], diff --git a/src/app/core/models/auth.model.ts b/src/app/core/models/auth.model.ts index ec157b6caf1b292692a3d2889c31e2023351ca95..c3b13bf4441fc4fc7764d5c074442d80687e7f4b 100644 --- a/src/app/core/models/auth.model.ts +++ b/src/app/core/models/auth.model.ts @@ -66,7 +66,7 @@ export class LegacyAccount { } } -export interface UserService { +export interface IRequestedAccessService { dataset_id: number; dataset_name: string; status: string; @@ -74,3 +74,16 @@ export interface UserService { url_pattern: string; valid_until: Date; } + +export interface IRestrictedAccessService { + dataset_id: number; + dataset_name: string; + abstract: string; + service_url: string; +} + +export interface IMode { + id: number; + name: string; + abstract: string; +} diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts index facc8e86ea2803aa195b6ea83ab1c0407ed0d253..4b77e878da9ed4d6fd83ab993d18c709104062ef 100644 --- a/src/app/core/services/auth.service.ts +++ b/src/app/core/services/auth.service.ts @@ -6,7 +6,7 @@ import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { ILoginResponse, User, LegacyAccount } from '../models'; import { environment } from '../../../environments/environment'; -import { UserService } from '../models/auth.model'; +import { IRequestedAccessService, IRestrictedAccessService, IMode } from '../models/auth.model'; @Injectable() export class AuthService { @@ -78,8 +78,47 @@ export class AuthService { return this._user != null; } - getUserServices(): Observable<UserService[]> { - return this._http.get<UserService[]>(`${environment.middlewareLegacyAuth}get_user_service/`).pipe( + getUserServices(): Observable<IRequestedAccessService[]> { + return this._http.get<IRequestedAccessService[]>(`${environment.middlewareLegacyAuth}get_user_service/`).pipe( + map( + (res) => { + return res; + }, + (err) => { + throw err; + }, + ), + ); + } + + getRestrictedAccessService(): Observable<IRestrictedAccessService[]> { + return this._http.get<IRestrictedAccessService[]>(`${environment.middlewareLegacyAuth}get_services`).pipe( + map( + (res) => { + return res; + }, + (err) => { + throw err; + }, + ), + ); + } + + getModes(): Observable<IMode[]> { + return this._http.get<IMode[]>(`${environment.middlewareLegacyAuth}get_modes`).pipe( + map( + (res) => { + return res; + }, + (err) => { + throw err; + }, + ), + ); + } + + requestAccessToService(service) { + return this._http.post<any>(`${environment.middlewareLegacyAuth}add_user_service`, service).pipe( map( (res) => { return res; diff --git a/src/i18n/notification-messages/notification-messages.fr.ts b/src/i18n/notification-messages/notification-messages.fr.ts index 0d8c3498e8be8ff5300880107b55a0ebd52707ff..bbef064f2f0d269a77c5b3583d0bc300d0618838 100644 --- a/src/i18n/notification-messages/notification-messages.fr.ts +++ b/src/i18n/notification-messages/notification-messages.fr.ts @@ -31,4 +31,8 @@ export const messages = { existingAccount: 'Un compte existe déjà pour cet email.', uncompleteForm: 'Le formulaire est incomplet.', }, + userServices: { + addSuccess: 'Vous avez demandé l\'accès à', + addFailed: 'Une erreur est survenu lors de la demande d\'accès à ', + }, }; diff --git a/src/i18n/notification-messages/notification-messages.ts b/src/i18n/notification-messages/notification-messages.ts index 5320aab7b48943b8ec8394503ea3344db8ac6bce..ed8ba1e902ffecd5451e2b4f409f6d7511660709 100644 --- a/src/i18n/notification-messages/notification-messages.ts +++ b/src/i18n/notification-messages/notification-messages.ts @@ -30,4 +30,8 @@ export const messages = { existingAccount: 'An account already exists for that email.', uncompleteForm: 'Incomplete form.', }, + userServices: { + addSuccess: 'You requested access to ', + addFailed: 'An error occured while requesting access to ', + }, };