Skip to content
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

feat(web): upgrade to support v9.8.1 Firebase JS SDK #8235

Merged
merged 31 commits into from Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
29b1f61
feat(firebase_core): upgrade core web to v9 sdk
Ehesp Mar 8, 2022
9988b4c
fix database app usage
Ehesp Mar 8, 2022
faaba41
fix: fix tests
Ehesp Mar 8, 2022
19511a4
fix: formatting
Ehesp Mar 8, 2022
bc72147
Merge branch 'web-v9-sdk' into v9/core
russellwheatley May 16, 2022
53f0733
chore(core): update web firebase version
russellwheatley May 16, 2022
cf8b423
Merge branch 'v9/core' of github.com:FirebaseExtended/flutterfire int…
russellwheatley May 16, 2022
c81c509
Merge branch 'web-v9-sdk' into v9/core
russellwheatley May 23, 2022
35b27f7
feat(core, web): remove trigger after use
russellwheatley Jun 16, 2022
24a7ce1
chore(core, web): update API
russellwheatley Jun 16, 2022
40320cf
chore(core, web): update API
russellwheatley Jun 16, 2022
0cc3c6c
docs(core, web): update inline doc
russellwheatley Jun 16, 2022
6d8ee42
feat(core): add crossOrigin script
russellwheatley Jun 27, 2022
bbaaf16
fix analyzer
russellwheatley Jun 27, 2022
5933eab
fix(core, web): cannot build web apps without this update to database…
russellwheatley Jun 27, 2022
2a0f579
format
russellwheatley Jun 27, 2022
480d6b5
feat(firebase_firestore): upgrade to web v9 sdk (#8690)
russellwheatley Jun 27, 2022
60ea720
feat(functions, web): integrate web v9 SDK (#8922)
russellwheatley Jun 28, 2022
078092c
Merge branch 'web-v9-sdk' into v9/core
russellwheatley Jun 28, 2022
e0bff2f
feat(firebase_analytics): upgrade analytics web to v9 sdk (#8810)
russellwheatley Jun 28, 2022
471eb38
feat(firebase_remote_config): upgrade remote-config web to v9 sdk (#8…
russellwheatley Jun 29, 2022
9a4af59
feat(firebase_storage, web): upgrade storage web to v9 sdk (#8870)
russellwheatley Jun 29, 2022
b298b44
feat(firebase_performance): upgrade performance web to v9 sdk (#8868)
russellwheatley Jun 30, 2022
de987a7
feat(firebase_app_check): upgrade app_check web to v9 sdk. (#8816)
russellwheatley Jun 30, 2022
75d8ad3
refactor(firebase_database): upgrade database to integrate with modul…
russellwheatley Jun 30, 2022
f7bc21b
feat(app-installations): web v9 SDK implementation (#9007)
russellwheatley Jul 4, 2022
41b21ee
Update packages/cloud_firestore/cloud_firestore_platform_interface/li…
russellwheatley Jul 4, 2022
aee9725
Merge branch 'master' into v9/core
russellwheatley Jul 5, 2022
fd7f477
Update packages/cloud_firestore/cloud_firestore_platform_interface/li…
russellwheatley Jul 5, 2022
6e59f23
fix: import foundation
russellwheatley Jul 6, 2022
d3bccf8
fix: rm import
russellwheatley Jul 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
173 changes: 62 additions & 111 deletions packages/firebase_core/firebase_core_web/lib/src/firebase_core_web.dart
Expand Up @@ -10,15 +10,21 @@ class FirebaseWebService {
/// The name which matches the Firebase JS Web SDK postfix.
String name;

/// Naming of Firebase web products is different from Flutterfire plugins. This
/// property allows overriding of web naming to Flutterfire plugin naming.
String? override;

/// Creates a new [FirebaseWebService].
FirebaseWebService._(this.name);
FirebaseWebService._(this.name, [this.override]);
}

/// The entry point for accessing Firebase.
///
/// You can get an instance by calling [FirebaseCore.instance].
class FirebaseCoreWeb extends FirebasePlatform {
static Map<String, FirebaseWebService> _services = {};
static Map<String, FirebaseWebService> _services = {
'core': FirebaseWebService._('app', 'core'),
};

/// Internally registers a Firebase Service to be initialized.
static void registerService(String service) {
Expand All @@ -30,12 +36,6 @@ class FirebaseCoreWeb extends FirebasePlatform {
FirebasePlatform.instance = FirebaseCoreWeb();
}

/// Returns whether `requirejs` is within the global context (usually the
/// case in development).
bool get _isRequireJsDefined {
return context['require'] != null;
}

/// Returns the Firebase JS SDK Version to use.
///
/// You can override the supported version by attaching a version string to
Expand Down Expand Up @@ -75,118 +75,62 @@ class FirebaseCoreWeb extends FirebasePlatform {

/// Injects a `script` with a `src` dynamically into the head of the current
/// document.
Future<void> _injectSrcScript(String src) async {
Future<void> _injectSrcScript(String src, String windowVar) async {
ScriptElement script = ScriptElement();
script.type = 'text/javascript';
russellwheatley marked this conversation as resolved.
Show resolved Hide resolved
script.src = src;
script.async = true;
script.text = '''
window.ff_trigger_$windowVar = async (callback) => {
callback(await import("$src"));
};
''';

assert(document.head != null);
document.head!.append(script);
await script.onLoad.first;
Completer completer = Completer();

context.callMethod('ff_trigger_$windowVar', [
(module) {
context[windowVar] = module;
context.deleteProperty('ff_trigger_$windowVar');
completer.complete();
}
]);

await completer.future;
}

/// Initializes the Firebase JS SDKs by injecting them into the `head` of the
/// document when Firebase is initalized.
/// document when Firebase is initialized.
Future<void> _initializeCore() async {
// If Firebase is already available, core has already been initialized
// (or the user has added the scripts to their html file).
if (context['firebase'] != null) {
if (context['firebase_core'] != null) {
return;
}

String version = _firebaseSDKVersion;
List<String> ignored = _ignoredServiceScripts;

// This must be loaded first!
await _injectSrcScript(
'https://www.gstatic.com/firebasejs/$version/firebase-app.js',
);

await Future.wait(
_services.values.map((service) {
if (ignored.contains(service.name)) {
if (ignored.contains(service.override ?? service.name)) {
return Future.value();
}

return _injectSrcScript(
'https://www.gstatic.com/firebasejs/$version/firebase-${service.name}.js',
'firebase_${service.override ?? service.name}',
);
}),
);
}

/// In development (or manually added), requirejs is added to the window.
///
/// The Firebase JS SDKs define their modules via requirejs (if it exists),
/// otherwise they attach to the window. This code loads Firebase from
/// requirejs manually attaches it to the window.
Future<void> _initializeCoreRequireJs() async {
// If Firebase is already available, core has already been initialized
// (or the user has added the scripts to their html file).
if (context['firebase'] != null) {
return;
}

String version = _firebaseSDKVersion;
List<String> ignored = _ignoredServiceScripts;

// In dev, requirejs is loaded in
JsObject require = JsObject.fromBrowserObject(context['require']);
require.callMethod('config', [
JsObject.jsify({
'paths': {
'@firebase/app':
'https://www.gstatic.com/firebasejs/$version/firebase-app',
'@firebase/analytics':
'https://www.gstatic.com/firebasejs/$version/firebase-analytics',
'@firebase/app-check':
'https://www.gstatic.com/firebasejs/$version/firebase-app-check',
'@firebase/auth':
'https://www.gstatic.com/firebasejs/$version/firebase-auth',
'@firebase/firestore':
'https://www.gstatic.com/firebasejs/$version/firebase-firestore',
'@firebase/functions':
'https://www.gstatic.com/firebasejs/$version/firebase-functions',
'@firebase/messaging':
'https://www.gstatic.com/firebasejs/$version/firebase-messaging',
'@firebase/storage':
'https://www.gstatic.com/firebasejs/$version/firebase-storage',
'@firebase/database':
'https://www.gstatic.com/firebasejs/$version/firebase-database',
'@firebase/remote-config':
'https://www.gstatic.com/firebasejs/$version/firebase-remote-config',
'@firebase/performance':
'https://www.gstatic.com/firebasejs/$version/firebase-performance',
'@firebase/installations':
'https://www.gstatic.com/firebasejs/$version/firebase-installations',
},
})
]);

Completer completer = Completer();

List<String> services = ['@firebase/app'];
_services.values.forEach((service) {
if (!ignored.contains(service.name)) {
services.add('@firebase/${service.name}');
}
});

context.callMethod('require', [
JsObject.jsify(services),
(app) {
context['firebase'] = app;
completer.complete();
}
]);

await completer.future;
}

/// Returns all created [FirebaseAppPlatform] instances.
@override
List<FirebaseAppPlatform> get apps {
return firebase.apps.map(_createFromJsApp).toList(growable: false);
return guardNotInitialized(
() => firebase.apps.map(_createFromJsApp).toList(growable: false),
);
}

/// Initializes a new [FirebaseAppPlatform] instance by [name] and [options] and returns
Expand All @@ -199,21 +143,8 @@ class FirebaseCoreWeb extends FirebasePlatform {
String? name,
FirebaseOptions? options,
}) async {
if (!_isRequireJsDefined) {
await _initializeCore();
} else {
await _initializeCoreRequireJs();
}

try {
firebase.SDK_VERSION;
} catch (e) {
if (e
.toString()
.contains("Cannot read property 'SDK_VERSION' of undefined")) {
throw coreNotInitialized();
}
}
await _initializeCore();
guardNotInitialized(() => firebase.SDK_VERSION);

assert(
() {
Expand Down Expand Up @@ -329,14 +260,8 @@ class FirebaseCoreWeb extends FirebasePlatform {
firebase.App app;

try {
app = firebase.app(name);
app = guardNotInitialized(() => firebase.app(name));
} catch (e) {
if ((e.toString().contains('Cannot read property') ||
e.toString().contains('Cannot read properties')) &&
e.toString().contains("'app'")) {
throw coreNotInitialized();
}

if (_getJSErrorCode(e) == 'app/no-app') {
throw noAppExists(name);
}
Expand All @@ -347,3 +272,29 @@ class FirebaseCoreWeb extends FirebasePlatform {
return _createFromJsApp(app);
}
}

/// Converts a Exception to a FirebaseAdminException.
Never _handleException(Object exception, StackTrace stackTrace) {
if (exception.toString().contains('of undefined')) {
throw coreNotInitialized();
}

Error.throwWithStackTrace(exception, stackTrace);
}

/// A generic guard wrapper for API calls to handle exceptions.
R guardNotInitialized<R>(R Function() cb) {
try {
final value = cb();

if (value is Future) {
return value.catchError(
_handleException,
) as R;
}

return value;
} catch (error, stackTrace) {
_handleException(error, stackTrace);
}
}
Expand Up @@ -6,4 +6,4 @@
part of firebase_core_web;

/// The currently supported Firebase JS SDK version.
const String supportedFirebaseJsSdkVersion = '8.10.1';
const String supportedFirebaseJsSdkVersion = '9.8.1';
Expand Up @@ -5,10 +5,12 @@

// ignore_for_file: public_member_api_docs

import 'package:firebase_core_web/firebase_core_web_interop.dart';

import 'app_interop.dart';
import 'core_interop.dart';
import 'core.dart' as core_interop;
import 'utils/js.dart';
import 'utils/utils.dart';

/// A Firebase App holds the initialization information for a collection
/// of services.
Expand All @@ -31,5 +33,5 @@ class App extends JsObjectWrapper<AppJsImpl> {
}

/// Deletes the app and frees resources of all App's services.
Future delete() => handleThenable(jsObject.delete());
Future<void> delete() => handleThenable(core_interop.deleteApp(jsObject));
}
Expand Up @@ -5,16 +5,14 @@

// ignore_for_file: public_member_api_docs

@JS('firebase.app')
@JS('firebase_core')
library firebase_interop.core.app;

import 'package:js/js.dart';
import 'core_interop.dart';
import 'utils/es6_interop.dart';

@JS('App')
@JS('FirebaseApp')
abstract class AppJsImpl {
external String get name;
external FirebaseOptions get options;
external PromiseJsImpl<void> delete();
}
Expand Up @@ -14,7 +14,8 @@ export 'app.dart';
export 'app_interop.dart';
export 'core_interop.dart';

List<App> get apps => firebase_interop.apps
List<App> get apps => firebase_interop
.getApps()
// explicitly typing the param as dynamic to work-around
// https://github.com/dart-lang/sdk/issues/33537
// ignore: unnecessary_lambdas
Expand Down Expand Up @@ -49,11 +50,10 @@ App initializeApp({
name,
),
);
// TODO if error - firebase not loaded?
}

App app([String? name]) {
return App.getInstance(
name != null ? firebase_interop.app(name) : firebase_interop.app(),
name != null ? firebase_interop.getApp(name) : firebase_interop.getApp(),
);
}
Expand Up @@ -5,15 +5,16 @@

// ignore_for_file: public_member_api_docs, non_constant_identifier_names

@JS('firebase')
@JS('firebase_core')
library firebase_interop.core;

import 'package:firebase_core_web/firebase_core_web_interop.dart';
import 'package:js/js.dart';

import 'app_interop.dart';

@JS()
external List<AppJsImpl> get apps;
external List<AppJsImpl> getApps();

/// The current SDK version.
///
Expand All @@ -25,7 +26,10 @@ external String get SDK_VERSION;
external AppJsImpl initializeApp(FirebaseOptions options, [String? name]);

@JS()
external AppJsImpl app([String? name]);
external AppJsImpl getApp([String? name]);

@JS()
external PromiseJsImpl<void> deleteApp(AppJsImpl app);

/// FirebaseError is a subclass of the standard Error object.
/// In addition to a message string, it contains a string-valued code.
Expand Down
Expand Up @@ -58,6 +58,14 @@ void main() {
});

test('should throw exception if no named app was found', () async {
(js.context['firebase_core'] as js.JsObject)['getApp'] =
js.allowInterop((String name) {
final dynamic error = js_util.newObject();
js_util.setProperty(error, 'name', 'FirebaseError');
js_util.setProperty(error, 'code', 'app/no-app');
throw error;
});

await expectLater(
() => Firebase.app('foo'),
throwsA(noAppExists('foo')),
Expand Down