Skip to content

Commit 7f0f952

Browse files
authored
Merge pull request #2705 from ORCID/lmendoza/PD-3822-qa
PD-3822
2 parents a38eaef + f5faa4f commit 7f0f952

File tree

44 files changed

+184
-67
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+184
-67
lines changed

src/app/app.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { TitleService } from './core/title-service/title.service'
1616
import { ZendeskService } from './core/zendesk/zendesk.service'
1717
import { ERROR_REPORT } from './errors'
1818
import { TogglzService } from './core/togglz/togglz.service'
19-
import { TogglzFlag } from './core/togglz/togglz-flags.enum'
19+
import { TogglzFlag } from './types/config.endpoint'
2020
import { NewRelicService } from './core/new-relic/new-relic.service'
2121

2222
@Component({

src/app/authorize/components/form-authorize/form-authorize.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { GoogleTagManagerService } from '../../../core/google-tag-manager/google
3737
import { Title } from '@angular/platform-browser'
3838
import { TogglzService } from 'src/app/core/togglz/togglz.service'
3939
import { OauthURLSessionManagerService } from 'src/app/core/oauth-urlsession-manager/oauth-urlsession-manager.service'
40-
import { TogglzFlag } from 'src/app/core/togglz/togglz-flags.enum'
40+
import { TogglzFlag } from 'src/app/types/config.endpoint'
4141

4242
@Component({
4343
selector: 'app-form-authorize',

src/app/authorize/components/oauth-error/oauth-error.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { LegacyOauthRequestInfoForm as RequestInfoForm } from 'src/app/types/req
1010
import { UserSession } from 'src/app/types/session.local'
1111
import { RumJourneyEventService } from 'src/app/rum/service/customEvent.service'
1212
import { JourneyType } from 'src/app/rum/journeys/types'
13-
import { TogglzFlag } from 'src/app/core/togglz/togglz-flags.enum'
13+
import { TogglzFlag } from 'src/app/types/config.endpoint'
1414

1515
@Component({
1616
selector: 'app-oauth-error',

src/app/authorize/pages/authorize/authorize.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { UserSession } from 'src/app/types/session.local'
2222
import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal'
2323
import { OauthURLSessionManagerService } from 'src/app/core/oauth-urlsession-manager/oauth-urlsession-manager.service'
2424
import { FeatureLoggerService } from 'src/app/core/logging/feature-logger.service'
25-
import { TogglzFlag } from 'src/app/core/togglz/togglz-flags.enum'
25+
import { TogglzFlag } from 'src/app/types/config.endpoint'
2626

2727
@Component({
2828
templateUrl: './authorize.component.html',
Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
11
import { RecaptchaDirective } from './recaptcha.directive'
22

33
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
4+
import { of } from 'rxjs'
5+
import { TogglzService } from 'src/app/core/togglz/togglz.service'
6+
import { ErrorHandlerService } from 'src/app/core/error-handler/error-handler.service'
7+
import { ConfigMessageKey } from 'src/app/types/config.endpoint'
8+
import { WINDOW } from '../window'
49

510
describe('RecaptchaDirective', () => {
611
it('should create an instance', () => {
7-
const directive = new RecaptchaDirective(null, null, null, null, null)
12+
const togglzServiceMock = {
13+
getConfigurationOf: (_key: ConfigMessageKey) => of('key'),
14+
} as unknown as TogglzService
15+
const errorHandlerMock = {
16+
handleError: () => of(null),
17+
} as unknown as ErrorHandlerService
18+
const directive = new RecaptchaDirective(
19+
{} as any,
20+
{} as any,
21+
{} as any,
22+
{} as any,
23+
errorHandlerMock,
24+
togglzServiceMock
25+
)
826
expect(directive).toBeTruthy()
927
})
1028
})

src/app/cdk/recaptcha/recaptcha.directive.ts

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ import {
1717
import { WINDOW } from '../window'
1818
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
1919
import { ErrorHandlerService } from 'src/app/core/error-handler/error-handler.service'
20+
import { TogglzService } from 'src/app/core/togglz/togglz.service'
21+
import { ConfigMessageKey } from 'src/app/types/config.endpoint'
22+
import { take } from 'rxjs/operators'
2023

2124
export interface ReCaptchaConfig {
25+
sitekey: string
2226
theme?: 'dark' | 'light'
2327
type?: 'audio' | 'image'
2428
size?: 'compact' | 'normal'
@@ -28,6 +32,7 @@ export interface ReCaptchaConfig {
2832
interface WindowWithCaptcha extends Window {
2933
grecaptcha: {
3034
render: (ElementRef, ReCaptchaConfig) => number
35+
reset: (opt_widget_id?: number) => void
3136
}
3237
orcidReCaptchaOnLoad: () => void
3338
}
@@ -46,15 +51,19 @@ interface WindowWithCaptcha extends Window {
4651
export class RecaptchaDirective implements OnInit, ControlValueAccessor {
4752
@Output() captchaFail = new EventEmitter<boolean>()
4853
@Output() captchaLoaded = new EventEmitter<number>()
49-
private onChange: (value: string) => void
50-
private onTouched: (value: string) => void
54+
private onChange: (value: string | null) => void
55+
private onTouched: (value: string | null) => void
56+
private renderId: number | null = null
57+
private lastConfig: ReCaptchaConfig | null = null
58+
private renderRetried = false
5159

5260
constructor(
5361
@Inject(WINDOW) private window: WindowWithCaptcha,
5462
@Inject(LOCALE_ID) public locale: string,
5563
private ngZone: NgZone,
5664
private element: ElementRef,
57-
private _errorHandler: ErrorHandlerService
65+
private _errorHandler: ErrorHandlerService,
66+
private _togglzService: TogglzService
5867
) {}
5968

6069
ngOnInit() {
@@ -64,28 +73,86 @@ export class RecaptchaDirective implements OnInit, ControlValueAccessor {
6473

6574
registerReCaptchaCallback() {
6675
this.window.orcidReCaptchaOnLoad = () => {
67-
const config = {
68-
sitekey: runtimeEnvironment.GOOGLE_RECAPTCHA,
69-
callback: (response: string) => {
70-
this.ngZone.run(() => this.onSuccess(response))
71-
},
72-
73-
'expired-callback': (response: string) => {
74-
this.ngZone.run(() => this.onExpired())
75-
},
76-
'error-callback': () => {
77-
this.ngZone.run(() => this.onCaptchaFail())
78-
},
79-
}
76+
this._togglzService
77+
.getConfigurationOf(ConfigMessageKey.RECAPTCHA_WEB_KEY)
78+
.pipe(take(1))
79+
.subscribe({
80+
next: (siteKey: string) => {
81+
if (!siteKey) {
82+
this.ngZone.run(() => this.onCaptchaFail('missing_sitekey'))
83+
return
84+
}
85+
if (!this.window.grecaptcha || !this.window.grecaptcha.render) {
86+
this.ngZone.run(() => this.onCaptchaFail('grecaptcha_not_ready'))
87+
return
88+
}
89+
90+
if (this.renderId !== null) {
91+
this.window.grecaptcha.reset(this.renderId)
92+
this.captchaLoaded.emit(this.renderId)
93+
return
94+
}
95+
96+
const config: ReCaptchaConfig & {
97+
callback: (response: string) => void
98+
'expired-callback': (response: string) => void
99+
'error-callback': () => void
100+
} = {
101+
sitekey: siteKey,
102+
callback: (response: string) => {
103+
this.ngZone.run(() => this.onSuccess(response))
104+
},
105+
'expired-callback': (_response: string) => {
106+
this.ngZone.run(() => this.onExpired())
107+
},
108+
'error-callback': () => {
109+
this.handleRenderError('recaptcha_error_callback')
110+
},
111+
}
112+
113+
this.lastConfig = config
114+
this.renderRetried = false
115+
this.renderCaptcha(config)
116+
},
117+
error: () => {
118+
this.ngZone.run(() => this.onCaptchaFail('config_fetch_error'))
119+
},
120+
})
121+
}
122+
}
123+
124+
private renderCaptcha(config: ReCaptchaConfig) {
125+
try {
80126
const id = this.render(this.element.nativeElement, config)
127+
this.renderId = id
81128
this.captchaLoaded.emit(id)
129+
} catch (_err) {
130+
this.handleRenderError('render_exception')
131+
}
132+
}
133+
134+
private handleRenderError(reason: string) {
135+
if (!this.renderRetried && this.window.grecaptcha && this.lastConfig) {
136+
this.renderRetried = true
137+
try {
138+
const id = this.render(this.element.nativeElement, this.lastConfig)
139+
this.renderId = id
140+
this.captchaLoaded.emit(id)
141+
return
142+
} catch (_err) {
143+
// fall through
144+
}
82145
}
146+
this.ngZone.run(() => this.onCaptchaFail(reason))
83147
}
84148

85149
onExpired() {
86150
this.ngZone.run(() => {
87151
this.onChange(null)
88152
this.onTouched(null)
153+
if (this.renderId !== null && this.window.grecaptcha) {
154+
this.window.grecaptcha.reset(this.renderId)
155+
}
89156
})
90157
}
91158

@@ -94,12 +161,12 @@ export class RecaptchaDirective implements OnInit, ControlValueAccessor {
94161
this.onTouched(token)
95162
}
96163

97-
onCaptchaFail() {
164+
onCaptchaFail(reason: string = 'captchaFail') {
98165
this.captchaFail.emit(true)
99-
this._errorHandler.handleError(new Error('captchaFail')).subscribe()
166+
this._errorHandler.handleError(new Error(reason)).subscribe()
100167
}
101168

102-
private render(element: HTMLElement, config): number {
169+
private render(element: HTMLElement, config: ReCaptchaConfig): number {
103170
return this.window.grecaptcha.render(element, config)
104171
}
105172

src/app/cdk/side-bar/modals/modal-email/modal-email.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { ModalComponent } from 'src/app/cdk/modal/modal/modal.component'
2323
import { PlatformInfoService } from 'src/app/cdk/platform-info'
2424
import { SnackbarService } from 'src/app/cdk/snackbar/snackbar.service'
2525
import { RecordEmailsService } from 'src/app/core/record-emails/record-emails.service'
26-
import { TogglzFlag } from 'src/app/core/togglz/togglz-flags.enum'
26+
import { TogglzFlag } from 'src/app/types/config.endpoint'
2727
import { TogglzService } from 'src/app/core/togglz/togglz.service'
2828
import { UserInfoService } from 'src/app/core/user-info/user-info.service'
2929
import {

src/app/cdk/side-bar/side-bar/side-bar.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { ModalWebsitesComponent } from '../modals/modal-websites/modal-websites.
2929
import { ActivatedRoute } from '@angular/router'
3030
import { RecordUtil } from 'src/app/shared/utils/record.util'
3131
import { TogglzService } from 'src/app/core/togglz/togglz.service'
32-
import { TogglzFlag } from 'src/app/core/togglz/togglz-flags.enum'
32+
import { TogglzFlag } from 'src/app/types/config.endpoint'
3333

3434
@Component({
3535
selector: 'app-side-bar',

src/app/core/header-compact/header-compact.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
shareReplay,
1111
} from 'rxjs/operators'
1212
import { TogglzService } from '../togglz/togglz.service'
13-
import { TogglzFlag } from '../togglz/togglz-flags.enum'
13+
import { TogglzFlag } from 'src/app/types/config.endpoint'
1414
import { isValidOrcidFormat } from 'src/app/constants'
1515
import { WINDOW } from 'src/app/cdk/window'
1616

src/app/core/login-interstitials-manager/abstractions/login-abstract-interstitial-manager.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
BaseInterstitialDialogOutput,
1515
} from './dialog-interface'
1616
import { TogglzService } from '../../togglz/togglz.service'
17-
import { TogglzFlag } from '../../togglz/togglz-flags.enum'
17+
import { TogglzFlag } from 'src/app/types/config.endpoint'
1818

1919
export abstract class LoginBaseInterstitialManagerService<
2020
TInput extends BaseInterstitialDialogInput,

0 commit comments

Comments
 (0)