[UXP-10] Code refactoring

This commit is contained in:
Davide Negretti
2022-09-07 18:02:23 +02:00
parent b72b37a647
commit b6d6091c87
4 changed files with 115 additions and 103 deletions

View File

@@ -15,7 +15,8 @@ import {
ActivationEnd, ActivationEnd,
NavigationCancel, NavigationCancel,
NavigationEnd, NavigationEnd,
NavigationStart, ResolveEnd, NavigationStart,
ResolveEnd,
Router, Router,
} from '@angular/router'; } from '@angular/router';
@@ -48,8 +49,7 @@ import { BASE_THEME_NAME } from './shared/theme-support/theme.constants';
import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service';
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
import { getDefaultThemeConfig } from '../config/config.util'; import { getDefaultThemeConfig } from '../config/config.util';
import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; import { APP_CONFIG, AppConfig } from '../config/app-config.interface';
import { GoogleRecaptchaService } from './core/google-recaptcha/google-recaptcha.service';
@Component({ @Component({
selector: 'ds-app', selector: 'ds-app',
@@ -110,7 +110,6 @@ export class AppComponent implements OnInit, AfterViewInit {
private modalConfig: NgbModalConfig, private modalConfig: NgbModalConfig,
@Optional() private cookiesService: KlaroService, @Optional() private cookiesService: KlaroService,
@Optional() private googleAnalyticsService: GoogleAnalyticsService, @Optional() private googleAnalyticsService: GoogleAnalyticsService,
@Optional() private googleRecaptchaService: GoogleRecaptchaService,
) { ) {
if (!isEqual(environment, this.appConfig)) { if (!isEqual(environment, this.appConfig)) {

View File

@@ -5,8 +5,8 @@ import { isNotEmpty } from '../../shared/empty.util';
import { DOCUMENT } from '@angular/common'; import { DOCUMENT } from '@angular/common';
import { ConfigurationDataService } from '../data/configuration-data.service'; import { ConfigurationDataService } from '../data/configuration-data.service';
import { RemoteData } from '../data/remote-data'; import { RemoteData } from '../data/remote-data';
import { map, take } from 'rxjs/operators'; import { map, switchMap, take } from 'rxjs/operators';
import { combineLatest, Observable, of } from 'rxjs'; import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import { CookieService } from '../services/cookie.service'; import { CookieService } from '../services/cookie.service';
import { NativeWindowRef, NativeWindowService } from '../services/window.service'; import { NativeWindowRef, NativeWindowService } from '../services/window.service';
@@ -20,25 +20,33 @@ export const CAPTCHA_NAME = 'google-recaptcha';
export class GoogleRecaptchaService { export class GoogleRecaptchaService {
private renderer: Renderer2; private renderer: Renderer2;
/**
* A Google Recaptcha site key
*/
captchaSiteKeyStr: string;
/**
* A Google Recaptcha site key
*/
captchaSiteKey$: Observable<string>;
/**
* A Google Recaptcha mode
*/
captchaMode$: Observable<string> = of('invisible');
/** /**
* A Google Recaptcha version * A Google Recaptcha version
*/ */
captchaVersion$: Observable<string> = of(''); private captchaVersionSubject$ = new BehaviorSubject<string>(null);
/**
* The Google Recaptcha Key
*/
private captchaKeySubject$ = new BehaviorSubject<string>(null);
/**
* The Google Recaptcha mode
*/
private captchaModeSubject$ = new BehaviorSubject<string>(null);
captchaKey(): Observable<string> {
return this.captchaKeySubject$.asObservable();
}
captchaMode(): Observable<string> {
return this.captchaModeSubject$.asObservable();
}
captchaVersion(): Observable<string> {
return this.captchaVersionSubject$.asObservable();
}
constructor( constructor(
private cookieService: CookieService, private cookieService: CookieService,
@@ -66,38 +74,48 @@ export class GoogleRecaptchaService {
} }
loadRecaptchaProperties() { loadRecaptchaProperties() {
const recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe( const recaptchaKeyRD$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
take(1),
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
); );
const recaptchaVersion$ = this.configService.findByPropertyName('google.recaptcha.version').pipe( const recaptchaVersionRD$ = this.configService.findByPropertyName('google.recaptcha.version').pipe(
take(1),
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
); );
const recaptchaMode$ = this.configService.findByPropertyName('google.recaptcha.mode').pipe( const recaptchaModeRD$ = this.configService.findByPropertyName('google.recaptcha.mode').pipe(
take(1),
getFirstCompletedRemoteData(), getFirstCompletedRemoteData(),
); );
combineLatest(recaptchaVersion$, recaptchaMode$, recaptchaKey$).subscribe(([recaptchaVersion, recaptchaMode, recaptchaKey]) => { combineLatest([recaptchaVersionRD$, recaptchaModeRD$, recaptchaKeyRD$]).subscribe(([recaptchaVersionRD, recaptchaModeRD, recaptchaKeyRD]) => {
if (this.cookieService.get('klaro-anonymous') && this.cookieService.get('klaro-anonymous')[CAPTCHA_NAME]) {
if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) { if (
this.captchaSiteKeyStr = recaptchaKey?.payload?.values[0]; this.cookieService.get('klaro-anonymous') && this.cookieService.get('klaro-anonymous')[CAPTCHA_NAME] &&
this.captchaSiteKey$ = of(recaptchaKey?.payload?.values[0]); recaptchaKeyRD.hasSucceeded && recaptchaVersionRD.hasSucceeded &&
isNotEmpty(recaptchaVersionRD.payload?.values) && isNotEmpty(recaptchaKeyRD.payload?.values)
) {
const key = recaptchaKeyRD.payload?.values[0];
const version = recaptchaVersionRD.payload?.values[0];
this.captchaKeySubject$.next(key);
this.captchaVersionSubject$.next(version);
let captchaUrl;
switch (version) {
case 'v3':
if (recaptchaKeyRD.hasSucceeded && isNotEmpty(recaptchaKeyRD.payload?.values)) {
captchaUrl = this.buildCaptchaUrl(key);
this.captchaModeSubject$.next('invisible');
} }
if (recaptchaVersion.hasSucceeded && isNotEmpty(recaptchaVersion?.payload?.values[0]) && recaptchaVersion?.payload?.values[0] === 'v3') { break;
this.captchaVersion$ = of('v3'); case 'v2':
if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) { if (recaptchaModeRD.hasSucceeded && isNotEmpty(recaptchaModeRD.payload?.values)) {
this.loadScript(this.buildCaptchaUrl(recaptchaKey?.payload?.values[0])); captchaUrl = 'https://www.google.com/recaptcha/api.js';
this.captchaModeSubject$.next(recaptchaModeRD.payload?.values[0]);
} }
} else { break;
this.captchaVersion$ = of('v2'); default:
const captchaUrl = 'https://www.google.com/recaptcha/api.js'; // TODO handle error
if (recaptchaMode.hasSucceeded && isNotEmpty(recaptchaMode?.payload?.values[0])) { }
this.captchaMode$ = of(recaptchaMode?.payload?.values[0]); if (captchaUrl) {
this.loadScript(captchaUrl); this.loadScript(captchaUrl);
} }
} }
}
}); });
} }
@@ -105,23 +123,21 @@ export class GoogleRecaptchaService {
* Returns an observable of string * Returns an observable of string
* @param action action is the process type in which used to protect multiple spam REST calls * @param action action is the process type in which used to protect multiple spam REST calls
*/ */
public async getRecaptchaToken (action) { public getRecaptchaToken(action) {
return await grecaptcha.execute(this.captchaSiteKeyStr, {action: action}); return this.captchaKey().pipe(
switchMap((key) => grecaptcha.execute(key, {action: action}))
);
} }
/** /**
* Returns an observable of string * Returns an observable of string
*/ */
public async executeRecaptcha () { public executeRecaptcha() {
return await grecaptcha.execute(); return of(grecaptcha.execute());
} }
/** public getRecaptchaTokenResponse () {
* Returns an observable of string return grecaptcha.getResponse();
* @param action action is the process type in which used to protect multiple spam REST calls
*/
public async getRecaptchaTokenResponse () {
return await grecaptcha.getResponse();
} }
/** /**

View File

@@ -24,8 +24,8 @@
<div class="col-12"> <div class="col-12">
{{MESSAGE_PREFIX + '.email.hint' |translate}} {{MESSAGE_PREFIX + '.email.hint' |translate}}
</div> </div>
<div class="col-12" *ngIf="(googleRecaptchaService.captchaVersion$ | async) === 'v2'"> <div class="col-12" *ngIf="(googleRecaptchaService.captchaVersion() | async) === 'v2'">
<ds-google-recaptcha [captchaMode]="(googleRecaptchaService.captchaMode$ | async)" (executeRecaptcha)="register($event)"></ds-google-recaptcha> <ds-google-recaptcha [captchaMode]="(googleRecaptchaService.captchaMode() | async)" (executeRecaptcha)="register($event)"></ds-google-recaptcha>
</div> </div>
</div> </div>
@@ -33,12 +33,12 @@
</div> </div>
</form> </form>
<button *ngIf="(googleRecaptchaService.captchaVersion$ | async) === 'v2' && (googleRecaptchaService.captchaMode$ | async) === 'invisible'" <button *ngIf="(googleRecaptchaService.captchaVersion() | async) === 'v2' && (googleRecaptchaService.captchaMode() | async) === 'invisible'"
class="btn btn-primary" class="btn btn-primary"
[disabled]="form.invalid" [disabled]="form.invalid"
(click)="executeRecaptcha()"> (click)="executeRecaptcha()">
{{ MESSAGE_PREFIX + '.submit' | translate }}</button> {{ MESSAGE_PREFIX + '.submit' | translate }}</button>
<button *ngIf="!((googleRecaptchaService.captchaVersion$ | async) === 'v2' && (googleRecaptchaService.captchaMode$ | async) === 'invisible')" <button *ngIf="!((googleRecaptchaService.captchaVersion() | async) === 'v2' && (googleRecaptchaService.captchaMode() | async) === 'invisible')"
class="btn btn-primary" class="btn btn-primary"
[disabled]="form.invalid" [disabled]="form.invalid"
(click)="register()"> (click)="register()">

View File

@@ -7,12 +7,12 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'
import { Registration } from '../core/shared/registration.model'; import { Registration } from '../core/shared/registration.model';
import { RemoteData } from '../core/data/remote-data'; import { RemoteData } from '../core/data/remote-data';
import { ConfigurationDataService } from '../core/data/configuration-data.service'; import { ConfigurationDataService } from '../core/data/configuration-data.service';
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
import { ConfigurationProperty } from '../core/shared/configuration-property.model'; import { ConfigurationProperty } from '../core/shared/configuration-property.model';
import { isNotEmpty } from '../shared/empty.util'; import { isNotEmpty } from '../shared/empty.util';
import { combineLatest, Observable, of, switchMap } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service'; import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service';
import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'ds-register-email-form', selector: 'ds-register-email-form',
@@ -39,7 +39,14 @@ export class RegisterEmailFormComponent implements OnInit {
*/ */
registrationVerification = false; registrationVerification = false;
recaptchaKey$: Observable<any>; captchaVersion(): Observable<string> {
return this.googleRecaptchaService.captchaVersion();
}
captchaMode(): Observable<string> {
return this.googleRecaptchaService.captchaMode();
}
constructor( constructor(
private epersonRegistrationService: EpersonRegistrationService, private epersonRegistrationService: EpersonRegistrationService,
@@ -61,14 +68,9 @@ export class RegisterEmailFormComponent implements OnInit {
], ],
}) })
}); });
this.recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
getFirstSucceededRemoteDataPayload(),
);
this.configService.findByPropertyName('registration.verification.enabled').pipe( this.configService.findByPropertyName('registration.verification.enabled').pipe(
getFirstCompletedRemoteData(), getFirstSucceededRemoteDataPayload(),
map((res: RemoteData<ConfigurationProperty>) => { map((res: ConfigurationProperty) => res?.values[0].toLowerCase() === 'true')
return res.hasSucceeded && res.payload && isNotEmpty(res.payload.values) && res.payload.values[0].toLowerCase() === 'true';
})
).subscribe((res: boolean) => { ).subscribe((res: boolean) => {
this.registrationVerification = res; this.registrationVerification = res;
}); });
@@ -77,38 +79,36 @@ export class RegisterEmailFormComponent implements OnInit {
/** /**
* execute the captcha function for v2 invisible * execute the captcha function for v2 invisible
*/ */
async executeRecaptcha() { executeRecaptcha() {
await this.googleRecaptchaService.executeRecaptcha(); console.log('executeRecaptcha');
this.googleRecaptchaService.executeRecaptcha();
} }
/** /**
* Register an email address * Register an email address
*/ */
async register(tokenV2 = null) { register(tokenV2 = null) {
if (!this.form.invalid) { if (!this.form.invalid) {
if (this.registrationVerification) { if (this.registrationVerification) {
let token; combineLatest([this.captchaVersion(), this.captchaMode()]).pipe(
let captchaVersion; switchMap(([captchaVersion, captchaMode]) => {
let captchaMode;
this.googleRecaptchaService.captchaVersion$.subscribe(res => {
captchaVersion = res;
});
this.googleRecaptchaService.captchaMode$.subscribe(res => {
captchaMode = res;
});
if (captchaVersion === 'v3') { if (captchaVersion === 'v3') {
token = await this.googleRecaptchaService.getRecaptchaToken('register_email'); return this.googleRecaptchaService.getRecaptchaToken('register_email');
} else if (captchaMode === 'checkbox') { } else if (captchaMode === 'checkbox') {
token = await this.googleRecaptchaService.getRecaptchaTokenResponse(); return this.googleRecaptchaService.getRecaptchaTokenResponse();
} else { } else {
token = tokenV2; return of(tokenV2);
} }
}),
).subscribe((token) => {
if (isNotEmpty(token)) { if (isNotEmpty(token)) {
this.registration(token); this.registration(token);
} else { } else {
this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`), this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`),
this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`)); this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`));
} }
}
);
} else { } else {
this.registration(); this.registration();
} }
@@ -119,12 +119,9 @@ export class RegisterEmailFormComponent implements OnInit {
* Registration of an email address * Registration of an email address
*/ */
registration(captchaToken = null) { registration(captchaToken = null) {
let registerEmail$; let registerEmail$ = captchaToken ?
if (captchaToken) { this.epersonRegistrationService.registerEmail(this.email.value, captchaToken) :
registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value, captchaToken); this.epersonRegistrationService.registerEmail(this.email.value);
} else {
registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value);
}
registerEmail$.subscribe((response: RemoteData<Registration>) => { registerEmail$.subscribe((response: RemoteData<Registration>) => {
if (response.hasSucceeded) { if (response.hasSucceeded) {
this.notificationService.success(this.translateService.get(`${this.MESSAGE_PREFIX}.success.head`), this.notificationService.success(this.translateService.get(`${this.MESSAGE_PREFIX}.success.head`),