-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Revert "#167410: _initCalled completed twice (#9694)" #11084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
db8d276
a67da7f
647175a
3f937da
fd359e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,36 +65,8 @@ void main() { | |
| await plugin.init( | ||
| const InitParameters(clientId: 'some-non-null-client-id'), | ||
| ); | ||
| }); | ||
|
|
||
| testWidgets('throws if init is called twice', (_) async { | ||
| await plugin.init( | ||
| const InitParameters(clientId: 'some-non-null-client-id'), | ||
| ); | ||
|
|
||
| // Calling init() a second time should throw state error | ||
| expect( | ||
| () => plugin.init( | ||
| const InitParameters(clientId: 'some-non-null-client-id'), | ||
| ), | ||
| throwsStateError, | ||
| ); | ||
| }); | ||
|
|
||
| testWidgets('throws if init is called twice synchronously', (_) async { | ||
| final Future<void> firstInit = plugin.init( | ||
| const InitParameters(clientId: 'some-non-null-client-id'), | ||
| ); | ||
|
|
||
| // Calling init() a second time synchronously should throw state error | ||
| expect( | ||
| () => plugin.init( | ||
| const InitParameters(clientId: 'some-non-null-client-id'), | ||
| ), | ||
| throwsStateError, | ||
| ); | ||
|
|
||
| await firstInit; | ||
| expect(plugin.initialized, completes); | ||
| }); | ||
|
Comment on lines
68
to
70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| testWidgets('asserts clientId is not null', (_) async { | ||
|
|
@@ -113,6 +85,35 @@ void main() { | |
| ); | ||
| }, throwsAssertionError); | ||
| }); | ||
|
|
||
| testWidgets('must be called for most of the API to work', (_) async { | ||
| expect(() async { | ||
| await plugin.attemptLightweightAuthentication( | ||
| const AttemptLightweightAuthenticationParameters(), | ||
| ); | ||
| }, throwsStateError); | ||
|
|
||
| expect(() async { | ||
| await plugin.clientAuthorizationTokensForScopes( | ||
| const ClientAuthorizationTokensForScopesParameters( | ||
| request: AuthorizationRequestDetails( | ||
| scopes: <String>[], | ||
| userId: null, | ||
| email: null, | ||
| promptIfUnauthorized: false, | ||
| ), | ||
| ), | ||
| ); | ||
| }, throwsStateError); | ||
|
|
||
| expect(() async { | ||
| await plugin.signOut(const SignOutParams()); | ||
| }, throwsStateError); | ||
|
|
||
| expect(() async { | ||
| await plugin.disconnect(const DisconnectParams()); | ||
| }, throwsStateError); | ||
| }); | ||
|
Comment on lines
+89
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| }); | ||
|
|
||
| group('support queries', () { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,7 +49,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| @visibleForTesting GisSdkClient? debugOverrideGisSdkClient, | ||
| @visibleForTesting | ||
| StreamController<AuthenticationEvent>? debugAuthenticationController, | ||
| }) : _debugOverrideGisSdkClient = debugOverrideGisSdkClient, | ||
| }) : _gisSdkClient = debugOverrideGisSdkClient, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| _authenticationController = | ||
| debugAuthenticationController ?? | ||
| StreamController<AuthenticationEvent>.broadcast() { | ||
|
|
@@ -68,31 +68,51 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
|
|
||
| // A future that completes when the JS loader is done. | ||
| late Future<void> _jsSdkLoadedFuture; | ||
|
|
||
| /// A completer used to track whether [init] has finished. | ||
| final Completer<void> _initCalled = Completer<void>(); | ||
|
|
||
| /// A boolean flag to track if [init] has been called. | ||
| /// | ||
| /// This is used to prevent race conditions when [init] is called multiple | ||
| /// times without awaiting. | ||
| bool _isInitCalled = false; | ||
| // A future that completes when the `init` call is done. | ||
| Completer<void>? _initCalled; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // A StreamController to communicate status changes from the GisSdkClient. | ||
| final StreamController<AuthenticationEvent> _authenticationController; | ||
|
|
||
| // The instance of [GisSdkClient] backing the plugin. | ||
| // Using late final ensures it can only be set once and throws if accessed before initialization. | ||
| late final GisSdkClient _gisSdkClient; | ||
| GisSdkClient? _gisSdkClient; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // An optional override for the GisSdkClient, used for testing. | ||
| final GisSdkClient? _debugOverrideGisSdkClient; | ||
| // A convenience getter to avoid using ! when accessing _gisSdkClient, and | ||
| // providing a slightly better error message when it is Null. | ||
| GisSdkClient get _gisClient { | ||
| assert( | ||
| _gisSdkClient != null, | ||
| 'GIS Client not initialized. ' | ||
| 'GoogleSignInPlugin::init() or GoogleSignInPlugin::initWithParams() ' | ||
| 'must be called before any other method in this plugin.', | ||
| ); | ||
| return _gisSdkClient!; | ||
| } | ||
|
Comment on lines
+82
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // This method throws if init or initWithParams hasn't been called at some | ||
| // point in the past. It is used by the [initialized] getter to ensure that | ||
| // users can't await on a Future that will never resolve. | ||
| void _assertIsInitCalled() { | ||
| if (_initCalled == null) { | ||
| throw StateError( | ||
| 'GoogleSignInPlugin::init() or GoogleSignInPlugin::initWithParams() ' | ||
| 'must be called before any other method in this plugin.', | ||
| ); | ||
| } | ||
|
Comment on lines
+95
to
+101
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| /// A future that resolves when the plugin is fully initialized. | ||
| /// | ||
| /// This ensures that the SDK has been loaded, and that the `init` method | ||
| /// has finished running. | ||
| Future<void> get _initialized => _initCalled.future; | ||
| @visibleForTesting | ||
| Future<void> get initialized { | ||
| _assertIsInitCalled(); | ||
| return Future.wait<void>(<Future<void>>[ | ||
| _jsSdkLoadedFuture, | ||
| _initCalled!.future, | ||
| ]); | ||
|
Comment on lines
+109
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| /// Stores the client ID if it was set in a meta-tag of the page. | ||
| @visibleForTesting | ||
|
|
@@ -105,14 +125,6 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
|
|
||
| @override | ||
| Future<void> init(InitParameters params) async { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| // Throw if init() is called more than once | ||
| if (_isInitCalled) { | ||
| throw StateError( | ||
| 'init() has already been called. Calling init() more than once results in undefined behavior.', | ||
| ); | ||
| } | ||
| _isInitCalled = true; | ||
|
|
||
| final String? appClientId = params.clientId ?? autoDetectedClientId; | ||
| assert( | ||
| appClientId != null, | ||
|
|
@@ -126,27 +138,27 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| 'serverClientId is not supported on Web.', | ||
| ); | ||
|
|
||
| _initCalled = Completer<void>(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| await _jsSdkLoadedFuture; | ||
|
|
||
| _gisSdkClient = | ||
| _debugOverrideGisSdkClient ?? | ||
| GisSdkClient( | ||
| clientId: appClientId!, | ||
| nonce: params.nonce, | ||
| hostedDomain: params.hostedDomain, | ||
| authenticationController: _authenticationController, | ||
| loggingEnabled: kDebugMode, | ||
| ); | ||
|
|
||
| _initCalled.complete(); | ||
| _gisSdkClient ??= GisSdkClient( | ||
| clientId: appClientId!, | ||
| nonce: params.nonce, | ||
| hostedDomain: params.hostedDomain, | ||
| authenticationController: _authenticationController, | ||
| loggingEnabled: kDebugMode, | ||
| ); | ||
|
Comment on lines
+145
to
+151
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| _initCalled!.complete(); // Signal that `init` is fully done. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| @override | ||
| Future<AuthenticationResults?>? attemptLightweightAuthentication( | ||
| AttemptLightweightAuthenticationParameters params, | ||
| ) { | ||
| _initialized.then((void value) { | ||
| _gisSdkClient.requestOneTap(); | ||
| initialized.then((void value) { | ||
| _gisClient.requestOneTap(); | ||
|
Comment on lines
+160
to
+161
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| }); | ||
| // One tap does not necessarily return immediately, and may never return, | ||
| // so clients should not await it. Return null to signal that. | ||
|
|
@@ -171,26 +183,26 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
|
|
||
| @override | ||
| Future<void> signOut(SignOutParams params) async { | ||
| await _initialized; | ||
| await initialized; | ||
|
|
||
| await _gisSdkClient.signOut(); | ||
| await _gisClient.signOut(); | ||
|
Comment on lines
+186
to
+188
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| @override | ||
| Future<void> disconnect(DisconnectParams params) async { | ||
| await _initialized; | ||
| await initialized; | ||
|
|
||
| await _gisSdkClient.disconnect(); | ||
| await _gisClient.disconnect(); | ||
|
Comment on lines
+193
to
+195
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| @override | ||
| Future<ClientAuthorizationTokenData?> clientAuthorizationTokensForScopes( | ||
| ClientAuthorizationTokensForScopesParameters params, | ||
| ) async { | ||
| await _initialized; | ||
| await initialized; | ||
| _validateScopes(params.request.scopes); | ||
|
|
||
| final String? token = await _gisSdkClient.requestScopes( | ||
| final String? token = await _gisClient.requestScopes( | ||
|
Comment on lines
+202
to
+205
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| params.request.scopes, | ||
| promptIfUnauthorized: params.request.promptIfUnauthorized, | ||
| userHint: params.request.userId, | ||
|
|
@@ -204,7 +216,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| Future<ServerAuthorizationTokenData?> serverAuthorizationTokensForScopes( | ||
| ServerAuthorizationTokensForScopesParameters params, | ||
| ) async { | ||
| await _initialized; | ||
| await initialized; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| _validateScopes(params.request.scopes); | ||
|
|
||
| // There is no way to know whether the flow will prompt in advance, so | ||
|
|
@@ -213,9 +225,7 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| return null; | ||
| } | ||
|
|
||
| final String? code = await _gisSdkClient.requestServerAuthCode( | ||
| params.request, | ||
| ); | ||
| final String? code = await _gisClient.requestServerAuthCode(params.request); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| return code == null | ||
| ? null | ||
| : ServerAuthorizationTokenData(serverAuthCode: code); | ||
|
|
@@ -237,8 +247,8 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| Future<void> clearAuthorizationToken( | ||
| ClearAuthorizationTokenParams params, | ||
| ) async { | ||
| await _initialized; | ||
| return _gisSdkClient.clearAuthorizationToken(params.accessToken); | ||
| await initialized; | ||
| return _gisClient.clearAuthorizationToken(params.accessToken); | ||
|
Comment on lines
+250
to
+251
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| @override | ||
|
|
@@ -268,13 +278,13 @@ class GoogleSignInPlugin extends GoogleSignInPlatform { | |
| configuration ?? GSIButtonConfiguration(); | ||
| return FutureBuilder<void>( | ||
| key: Key(config.hashCode.toString()), | ||
| future: _initialized, | ||
| future: initialized, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| builder: (BuildContext context, AsyncSnapshot<void> snapshot) { | ||
| if (snapshot.hasData) { | ||
| return FlexHtmlElementView( | ||
| viewType: 'gsi_login_button', | ||
| onElementCreated: (Object element) { | ||
| _gisSdkClient.renderButton(element, config); | ||
| _gisClient.renderButton(element, config); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| }, | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system | |
| for signing in with a Google account on Android, iOS and Web. | ||
| repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_web | ||
| issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 | ||
| version: 1.1.1 | ||
| version: 1.1.2 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| environment: | ||
| sdk: ^3.9.0 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changelog entry for version 1.1.2 correctly indicates the revert. This is in line with good release practices.