├── .analysis_options ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── dart_test.yaml ├── example ├── auth │ ├── index.dart │ └── index.html ├── index.html ├── realtime_database │ ├── index.dart │ └── index.html └── storage │ ├── index.dart │ └── index.html ├── lib ├── firebase.dart ├── firebase_io.dart └── src │ ├── app.dart │ ├── assets │ ├── README.md │ ├── assets.dart │ ├── assets_io.dart │ └── config.json.sample │ ├── auth.dart │ ├── database.dart │ ├── interop │ ├── app_interop.dart │ ├── auth_interop.dart │ ├── database_interop.dart │ ├── firebase_interop.dart │ ├── js_interop.dart │ └── storage_interop.dart │ ├── js.dart │ ├── storage.dart │ ├── top_level.dart │ └── utils.dart ├── pubspec.yaml ├── test ├── app_test.dart ├── app_test.html ├── auth_test.dart ├── auth_test.html ├── database_test.dart ├── database_test.html ├── io_test.dart ├── storage_test.dart ├── storage_test.html ├── test_util.dart └── utils_test.dart └── tool ├── create_config.dart └── travis.sh /.analysis_options: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: true 3 | linter: 4 | rules: 5 | #- always_declare_return_types 6 | #- always_specify_types 7 | - annotate_overrides 8 | #- avoid_as 9 | - avoid_empty_else 10 | - avoid_init_to_null 11 | #- avoid_return_types_on_setters 12 | - await_only_futures 13 | - camel_case_types 14 | - cancel_subscriptions 15 | #- close_sinks 16 | #- comment_references 17 | #- constant_identifier_names 18 | - control_flow_in_finally 19 | - empty_catches 20 | - empty_constructor_bodies 21 | - empty_statements 22 | - hash_and_equals 23 | - implementation_imports 24 | - iterable_contains_unrelated_type 25 | - library_names 26 | - library_prefixes 27 | - list_remove_unrelated_type 28 | #- non_constant_identifier_names 29 | - one_member_abstracts 30 | - only_throw_errors 31 | - overridden_fields 32 | - package_api_docs 33 | - package_names 34 | - package_prefixed_library_names 35 | - prefer_is_not_empty 36 | #- public_member_api_docs 37 | - slash_for_doc_comments 38 | #- sort_constructors_first 39 | - sort_unnamed_constructors_first 40 | - super_goes_last 41 | - test_types_in_equals 42 | - throw_in_finally 43 | #- type_annotate_public_apis 44 | - type_init_formals 45 | #- unawaited_futures 46 | - unnecessary_brace_in_string_interp 47 | - unnecessary_getters_setters 48 | - unrelated_type_equality_checks 49 | - valid_regexps 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .packages 3 | .pub/ 4 | build/ 5 | packages 6 | pubspec.lock 7 | 8 | # Files created by dart2js 9 | *.dart.js 10 | *.part.js 11 | *.js.deps 12 | *.js.map 13 | *.info.json 14 | 15 | # Directory created by dartdoc 16 | doc/api/ 17 | 18 | # JetBrains IDEs 19 | .idea/ 20 | *.iml 21 | *.ipr 22 | *.iws 23 | 24 | lib/src/assets/config.json 25 | lib/src/assets/service_account.json 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | sudo: false 3 | dart: 4 | - stable 5 | cache: 6 | directories: 7 | - $HOME/.pub-cache 8 | env: 9 | - TEST_PLATFORM=vm 10 | - TEST_PLATFORM=firefox 11 | addons: 12 | firefox: latest 13 | before_script: 14 | - "export DISPLAY=:99.0" 15 | - "sh -e /etc/init.d/xvfb start" 16 | - 't=0; until (xdpyinfo -display :99 &> /dev/null || test $t -gt 10); do sleep 1; let t=$t+1; done' 17 | script: ./tool/travis.sh 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.3 2 | 3 | - *BREAKING* This library is now deprecated and moved to [firebase-dart](https://github.com/firebase/firebase-dart/). 4 | - Update to Firebase 3.6.2. 5 | - Support for custom OAuth parameters with `GoogleAuthProvider.setCustomParameters()`, 6 | `FacebookAuthProvider.setCustomParameters()`, `GithubAuthProvider.setCustomParameters()` and 7 | `TwitterAuthProvider.setCustomParameters()` methods. 8 | - `AuthCredential` now has the `accessToken` and `secret` (Twitter only) properties. 9 | - Added an implementation of simple, server access to Firebase realtime database via `firebase_io.dart`. 10 | 11 | ## 0.2.2 12 | 13 | - Update to Firebase 3.4.0. 14 | - Added `isEqual()` method for `Query` class. 15 | - Strong mode fixes. 16 | - More docs. 17 | 18 | ## 0.2.1 19 | 20 | - Input params validation for methods which work with JS types. 21 | - Fixes in the lib. 22 | - More documentation and tests. 23 | - Readme update with information on how to run tests and examples. 24 | 25 | ## 0.2.0 26 | 27 | - *BREAKING* Exposing only one top-level library: `firebase.dart`. 28 | - *BREAKING* `ThenableReference` and `UploadTask` are having `future` property to handle `then()` and `catchError()`. 29 | - *BREAKING* `CustomMetadata` is now a Map. 30 | - *BREAKING* Storage APIs now expose `Uri` and `DateTime` instead of `String` where appropriate. 31 | - *BREAKING* Storage `onStateChanged` returns `Stream`. 32 | - Updates and fixes in examples 33 | - Fixes in the lib 34 | 35 | ## 0.1.0 36 | 37 | - Initial version of library 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Jana Moudrá 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED: Dart wrapper library for the new Firebase 2 | 3 | This library is now **deprecated** and moved to the official [firebase](https://pub.dartlang.org/packages/firebase). 4 | 5 | Please update your `pubspec.yaml` file to `firebase` dependency, do the `pub get`, update imports to `firebase` library and you are ready to go: 6 | 7 | ``` 8 | dependencies: 9 | firebase: '^3.0.0' 10 | ``` 11 | -------------------------------------------------------------------------------- /dart_test.yaml: -------------------------------------------------------------------------------- 1 | # This is a browser-only package. 2 | # Default to running in Dartium instead of the Dart VM. 3 | # NOTE: It's good to verify JS targets work by using 4 | # `pub run test -p dartium,firefox,chrome` etc before committing 5 | platforms: [vm, dartium] 6 | -------------------------------------------------------------------------------- /example/auth/index.dart: -------------------------------------------------------------------------------- 1 | library firebase3.example.auth; 2 | 3 | import 'dart:html'; 4 | 5 | import 'package:firebase3/firebase.dart' as fb; 6 | import 'package:firebase3/src/assets/assets.dart'; 7 | 8 | main() async { 9 | //Use for firebase3 package development only 10 | await config(); 11 | 12 | fb.initializeApp( 13 | apiKey: apiKey, 14 | authDomain: authDomain, 15 | databaseURL: databaseUrl, 16 | storageBucket: storageBucket); 17 | 18 | new AuthApp(); 19 | } 20 | 21 | class AuthApp { 22 | final fb.Auth auth; 23 | final FormElement registerForm; 24 | final InputElement email, password; 25 | final AnchorElement logout; 26 | final HeadingElement authInfo; 27 | final ParagraphElement error; 28 | 29 | AuthApp() 30 | : this.auth = fb.auth(), 31 | this.email = querySelector("#email"), 32 | this.password = querySelector("#password"), 33 | this.authInfo = querySelector("#auth_info"), 34 | this.error = querySelector("#register_form p"), 35 | this.logout = querySelector("#logout_btn"), 36 | this.registerForm = querySelector("#register_form") { 37 | logout.onClick.listen((e) { 38 | e.preventDefault(); 39 | auth.signOut(); 40 | }); 41 | 42 | this.registerForm.onSubmit.listen((e) { 43 | e.preventDefault(); 44 | var emailValue = email.value.trim(); 45 | var passwordvalue = password.value.trim(); 46 | _registerUser(emailValue, passwordvalue); 47 | }); 48 | 49 | // After opening 50 | if (auth.currentUser != null) { 51 | _setLayout(auth.currentUser); 52 | } 53 | 54 | // When auth state changes 55 | auth.onAuthStateChanged.listen((e) { 56 | fb.User u = e.user; 57 | _setLayout(u); 58 | }); 59 | } 60 | 61 | _registerUser(String email, String password) async { 62 | if (email.isNotEmpty && password.isNotEmpty) { 63 | try { 64 | await auth.createUserWithEmailAndPassword(email, password); 65 | } catch (e) { 66 | error.text = e.toString(); 67 | } 68 | } else { 69 | error.text = "Please fill correct e-mail and password."; 70 | } 71 | } 72 | 73 | void _setLayout(fb.User user) { 74 | if (user != null) { 75 | registerForm.style.display = "none"; 76 | logout.style.display = "block"; 77 | email.value = ""; 78 | password.value = ""; 79 | error.text = ""; 80 | authInfo.style.display = "block"; 81 | authInfo.text = user.email; 82 | } else { 83 | registerForm.style.display = "block"; 84 | authInfo.style.display = "none"; 85 | logout.style.display = "none"; 86 | authInfo.children.clear(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /example/auth/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Auth demo 6 | 11 | 13 | 14 | 15 |
16 |

Auth demo

17 | 24 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Examples 6 | 8 | 9 | 10 |
11 |

Examples

12 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /example/realtime_database/index.dart: -------------------------------------------------------------------------------- 1 | library firebase3.example.realtime_database; 2 | 3 | import 'dart:html'; 4 | 5 | import 'package:firebase3/firebase.dart' as fb; 6 | import 'package:firebase3/src/assets/assets.dart'; 7 | 8 | main() async { 9 | //Use for firebase3 package development only 10 | await config(); 11 | 12 | fb.initializeApp( 13 | apiKey: apiKey, 14 | authDomain: authDomain, 15 | databaseURL: databaseUrl, 16 | storageBucket: storageBucket); 17 | 18 | new MessagesApp().showMessages(); 19 | } 20 | 21 | class MessagesApp { 22 | final fb.DatabaseReference ref; 23 | final UListElement messages; 24 | final InputElement newMessage; 25 | final InputElement submit; 26 | final FormElement newMessageForm; 27 | 28 | MessagesApp() 29 | : ref = fb.database().ref("pkg_firebase3/examples/database"), 30 | messages = querySelector("#messages"), 31 | newMessage = querySelector("#new_message"), 32 | submit = querySelector('#submit'), 33 | newMessageForm = querySelector("#new_message_form") { 34 | newMessage.disabled = false; 35 | 36 | submit.disabled = false; 37 | 38 | this.newMessageForm.onSubmit.listen((e) async { 39 | e.preventDefault(); 40 | 41 | if (newMessage.value.trim().isNotEmpty) { 42 | var map = {"text": newMessage.value}; 43 | 44 | try { 45 | await ref.push(map).future; 46 | newMessage.value = ""; 47 | } catch (e) { 48 | print("Error while writing to db, $e"); 49 | } 50 | } 51 | }); 52 | } 53 | 54 | void showMessages() { 55 | this.ref.onChildAdded.listen((e) { 56 | fb.DataSnapshot datasnapshot = e.snapshot; 57 | 58 | var spanElement = new SpanElement()..text = datasnapshot.val()["text"]; 59 | 60 | var aElementDelete = new AnchorElement(href: "#") 61 | ..text = "Delete" 62 | ..onClick.listen((e) { 63 | e.preventDefault(); 64 | _deleteItem(datasnapshot); 65 | }); 66 | 67 | var aElementUpdate = new AnchorElement(href: "#") 68 | ..text = "To Uppercase" 69 | ..onClick.listen((e) { 70 | e.preventDefault(); 71 | _uppercaseItem(datasnapshot); 72 | }); 73 | 74 | var element = new LIElement() 75 | ..id = datasnapshot.key 76 | ..append(spanElement) 77 | ..append(aElementDelete) 78 | ..append(aElementUpdate); 79 | messages.append(element); 80 | }); 81 | 82 | this.ref.onChildChanged.listen((e) { 83 | fb.DataSnapshot datasnapshot = e.snapshot; 84 | var element = querySelector("#${datasnapshot.key} span"); 85 | 86 | if (element != null) { 87 | element.text = datasnapshot.val()["text"]; 88 | } 89 | }); 90 | 91 | this.ref.onChildRemoved.listen((e) { 92 | fb.DataSnapshot datasnapshot = e.snapshot; 93 | 94 | var element = querySelector("#${datasnapshot.key}"); 95 | 96 | if (element != null) { 97 | element.remove(); 98 | } 99 | }); 100 | } 101 | 102 | _deleteItem(fb.DataSnapshot datasnapshot) async { 103 | try { 104 | await this.ref.child(datasnapshot.key).remove(); 105 | } catch (e) { 106 | print("Error while deleting item, $e"); 107 | } 108 | } 109 | 110 | _uppercaseItem(fb.DataSnapshot datasnapshot) async { 111 | var value = datasnapshot.val(); 112 | var valueUppercase = value["text"].toString().toUpperCase(); 113 | value["text"] = valueUppercase; 114 | 115 | try { 116 | await this.ref.child(datasnapshot.key).update(value); 117 | } catch (e) { 118 | print("Error while updating item, $e"); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /example/realtime_database/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Realtime database demo 6 | 8 | 37 | 38 | 39 |
40 |

Messages demo

41 |
42 |
    43 |
    44 |
    45 | 46 | 47 |
    48 |
    49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/storage/index.dart: -------------------------------------------------------------------------------- 1 | library firebase3.example.storage; 2 | 3 | import 'dart:html'; 4 | 5 | import 'package:firebase3/firebase.dart' as fb; 6 | import 'package:firebase3/src/assets/assets.dart'; 7 | 8 | main() async { 9 | //Use for firebase3 package development only 10 | await config(); 11 | 12 | fb.initializeApp( 13 | apiKey: apiKey, 14 | authDomain: authDomain, 15 | databaseURL: databaseUrl, 16 | storageBucket: storageBucket); 17 | 18 | new ImageUploadApp(); 19 | } 20 | 21 | class ImageUploadApp { 22 | final fb.StorageReference ref; 23 | final InputElement _uploadImage; 24 | 25 | ImageUploadApp() 26 | : ref = fb.storage().ref("pkg_firebase3/examples/storage"), 27 | _uploadImage = querySelector("#upload_image") { 28 | _uploadImage.disabled = false; 29 | 30 | _uploadImage.onChange.listen((e) async { 31 | e.preventDefault(); 32 | var file = (e.target as FileUploadInputElement).files[0]; 33 | 34 | var customMetadata = {"location": "Prague", "owner": "You"}; 35 | var uploadTask = ref.child(file.name).put( 36 | file, 37 | new fb.UploadMetadata( 38 | contentType: file.type, customMetadata: customMetadata)); 39 | uploadTask.onStateChanged.listen((e) { 40 | querySelector("#message").text = 41 | "Transfered ${e.bytesTransferred}/${e.totalBytes}..."; 42 | }); 43 | 44 | try { 45 | var snapshot = await uploadTask.future; 46 | var filePath = snapshot.downloadURL; 47 | var metadata = snapshot.metadata.customMetadata; 48 | var image = new ImageElement(src: filePath.toString()); 49 | document.body.append(image); 50 | querySelector("#message").text = "Metadata: ${metadata.toString()}"; 51 | } catch (e) { 52 | print(e); 53 | } 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/storage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Storage demo 6 | 8 | 9 | 10 |
    11 |

    Upload image demo

    12 |
    13 | 14 |
    15 |

    16 |
    17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/firebase.dart: -------------------------------------------------------------------------------- 1 | /// Firebase is a global namespace from which all the Firebase 2 | /// services are accessed. 3 | /// 4 | /// See: . 5 | @Deprecated("use package:firebase instead") 6 | library firebase3; 7 | 8 | export 'src/app.dart'; 9 | export 'src/auth.dart'; 10 | export 'src/database.dart'; 11 | export 'src/storage.dart'; 12 | export 'src/top_level.dart'; 13 | -------------------------------------------------------------------------------- /lib/firebase_io.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:http/http.dart'; 5 | 6 | /// FirebaseClient wraps a REST client for a Firebase realtime database. 7 | /// 8 | /// The client supports authentication and GET, PUT, POST, DELETE 9 | /// and PATCH methods. 10 | @Deprecated("use package:firebase instead") 11 | class FirebaseClient { 12 | /// Auth credential. 13 | final String credential; 14 | final BaseClient _client; 15 | 16 | /// Creates a new FirebaseClient with [credential] and optional [client]. 17 | /// 18 | /// For credential you can either use Firebase app's secret or 19 | /// an authentication token. 20 | /// See: . 21 | FirebaseClient(this.credential, {BaseClient client}) 22 | : _client = client ?? new IOClient(); 23 | 24 | /// Creates a new anonymous FirebaseClient with optional [client]. 25 | FirebaseClient.anonymous({BaseClient client}) 26 | : credential = null, 27 | _client = client ?? new IOClient(); 28 | 29 | /// Reads data from database using a HTTP GET request. 30 | /// The response from a successful request contains a data being retrieved. 31 | /// 32 | /// See: . 33 | Future get(uri) => send('GET', uri); 34 | 35 | /// Writes or replaces data in database using a HTTP PUT request. 36 | /// The response from a successful request contains a data being written. 37 | /// 38 | /// See: . 39 | Future put(uri, json) => send('PUT', uri, json: json); 40 | 41 | /// Pushes data to database using a HTTP POST request. 42 | /// The response from a successful request contains a key of the new data 43 | /// being added. 44 | /// 45 | /// See: . 46 | Future post(uri, json) => send('POST', uri, json: json); 47 | 48 | /// Updates specific children at a location without overwriting existing data 49 | /// using a HTTP PATCH request. 50 | /// The response from a successful request contains a data being written. 51 | /// 52 | /// See: . 53 | Future patch(uri, json) => send('PATCH', uri, json: json); 54 | 55 | /// Deletes data from database using a HTTP DELETE request. 56 | /// The response from a successful request contains a JSON with [:null:]. 57 | /// 58 | /// See: . 59 | Future delete(uri) => send('DELETE', uri); 60 | 61 | /// Creates a request with a HTTP [method], [url] and optional data. 62 | /// The [url] can be either a `String` or `Uri`. 63 | Future send(String method, url, {json}) async { 64 | Uri uri = url is String ? Uri.parse(url) : url; 65 | 66 | var request = new Request(method, uri); 67 | if (credential != null) { 68 | request.headers['Authorization'] = "Bearer $credential"; 69 | } 70 | 71 | if (json != null) { 72 | request.headers['Content-Type'] = 'application/json'; 73 | request.body = JSON.encode(json); 74 | } 75 | 76 | var streamedResponse = await _client.send(request); 77 | var response = await Response.fromStream(streamedResponse); 78 | 79 | var bodyJson; 80 | try { 81 | bodyJson = JSON.decode(response.body); 82 | } on FormatException { 83 | var contentType = response.headers['content-type']; 84 | if (contentType != null && !contentType.contains('application/json')) { 85 | throw new Exception( 86 | 'Returned value was not JSON. Did the uri end with ".json"?'); 87 | } 88 | rethrow; 89 | } 90 | 91 | if (response.statusCode != 200) { 92 | if (bodyJson is Map) { 93 | var error = bodyJson['error']; 94 | if (error != null) { 95 | // TODO: wrap this in something helpful? 96 | throw error; 97 | } 98 | } 99 | throw bodyJson; 100 | } 101 | 102 | return bodyJson; 103 | } 104 | 105 | /// Closes the client and cleans up any associated resources. 106 | void close() => _client.close(); 107 | } 108 | -------------------------------------------------------------------------------- /lib/src/app.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'auth.dart'; 4 | import 'database.dart'; 5 | import 'interop/app_interop.dart'; 6 | import 'interop/firebase_interop.dart'; 7 | import 'js.dart'; 8 | import 'storage.dart'; 9 | import 'utils.dart'; 10 | 11 | export 'interop/firebase_interop.dart' show FirebaseError, FirebaseOptions; 12 | 13 | /// A Firebase App holds the initialization information for a collection 14 | /// of services. 15 | /// 16 | /// See: . 17 | class App extends JsObjectWrapper { 18 | /// Name of the app. 19 | String get name => jsObject.name; 20 | 21 | /// Options used during [firebase.initializeApp()]. 22 | FirebaseOptions get options => jsObject.options; 23 | 24 | /// Creates a new App from a [jsObject]. 25 | App.fromJsObject(AppJsImpl jsObject) : super.fromJsObject(jsObject); 26 | 27 | /// Returns [Auth] service. 28 | Auth auth() => new Auth.fromJsObject(jsObject.auth()); 29 | 30 | /// Returns [Database] service. 31 | Database database() => new Database.fromJsObject(jsObject.database()); 32 | 33 | /// Deletes the app and frees resources of all App's services. 34 | Future delete() => handleThenable(jsObject.delete()); 35 | 36 | /// Returns [Storage] service. 37 | Storage storage() => new Storage.fromJsObject(jsObject.storage()); 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/assets/README.md: -------------------------------------------------------------------------------- 1 | # For firebase3 package development only 2 | 3 | The contents of this directory are solely for use from within the `test` and 4 | `example` directories. You should **never** reference the contents of this 5 | directory from any other package or application. 6 | 7 | ## Use 8 | 9 | Copy `config.json.sample` to `config.json` within this directory. 10 | It must be populated with values from a Firebase application that you configure. 11 | See https://firebase.google.com/docs/web/setup for details. 12 | 13 | You'll notice that `config.json` is ignored in `.gitignore`. This is intentional 14 | to ensure private Firebase configuration data is not committed to the public 15 | repository. Be careful when sending pull requests to ensure your data is not 16 | included. 17 | -------------------------------------------------------------------------------- /lib/src/assets/assets.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:http/browser_client.dart' as http; 5 | 6 | Map _configVal; 7 | 8 | String get apiKey => _config['API_KEY']; 9 | String get authDomain => _config['AUTH_DOMAIN']; 10 | String get databaseUrl => _config['DATABASE_URL']; 11 | String get storageBucket => _config['STORAGE_BUCKET']; 12 | 13 | Map get _config { 14 | if (_configVal != null) { 15 | return _configVal; 16 | } 17 | throw new StateError('You must call config() first'); 18 | } 19 | 20 | Future config() async { 21 | if (_configVal != null) { 22 | return; 23 | } 24 | 25 | var client = new http.BrowserClient(); 26 | 27 | try { 28 | var response = 29 | await client.get('packages/firebase3/src/assets/config.json'); 30 | if (response.statusCode > 399) { 31 | throw new StateError( 32 | "Problem with server: ${response.statusCode} ${response.body}"); 33 | } 34 | 35 | var jsonString = response.body; 36 | _configVal = JSON.decode(jsonString) as Map; 37 | } catch (e) { 38 | print("Error getting `config.json`. Make sure it exists."); 39 | rethrow; 40 | } finally { 41 | client.close(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/assets/assets_io.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | import 'dart:isolate'; 5 | 6 | import 'package:googleapis_auth/auth_io.dart'; 7 | import 'package:http/http.dart'; 8 | 9 | const _firebaseScopes = const [ 10 | "https://www.googleapis.com/auth/firebase.database", 11 | "https://www.googleapis.com/auth/userinfo.email" 12 | ]; 13 | 14 | Future getAccessToken(IOClient client) async { 15 | var serviceAccountJsonPath = (await Isolate.resolvePackageUri( 16 | Uri.parse('package:firebase3/src/assets/service_account.json'))) 17 | .toFilePath(); 18 | 19 | var serviceAccountJsonString = 20 | new File(serviceAccountJsonPath).readAsStringSync(); 21 | 22 | var creds = new ServiceAccountCredentials.fromJson(serviceAccountJsonString); 23 | 24 | var accessCreds = await obtainAccessCredentialsViaServiceAccount( 25 | creds, _firebaseScopes, client); 26 | 27 | return accessCreds.accessToken; 28 | } 29 | 30 | Future getDatabaseUri() async { 31 | var serviceAccountJsonPath = (await Isolate.resolvePackageUri( 32 | Uri.parse('package:firebase3/src/assets/config.json'))) 33 | .toFilePath(); 34 | 35 | var jsonString = new File(serviceAccountJsonPath).readAsStringSync(); 36 | 37 | var json = JSON.decode(jsonString) as Map; 38 | 39 | return json['DATABASE_URL']; 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/assets/config.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "_FYI": "https://firebase.google.com/docs/web/setup", 3 | "_COPY_TO": "config.json", 4 | "API_KEY": "TODO", 5 | "AUTH_DOMAIN" : "TODO", 6 | "DATABASE_URL": "TODO", 7 | "STORAGE_BUCKET": "TODO" 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:js/js.dart'; 4 | 5 | import 'app.dart'; 6 | import 'interop/auth_interop.dart'; 7 | import 'interop/firebase_interop.dart' as firebase_interop; 8 | import 'js.dart'; 9 | import 'utils.dart'; 10 | 11 | export 'interop/auth_interop.dart' 12 | show ActionCodeInfo, ActionCodeEmail, AuthCredential; 13 | export 'interop/firebase_interop.dart' show UserProfile; 14 | 15 | /// User profile information, visible only to the Firebase project's apps. 16 | /// 17 | /// See: . 18 | class UserInfo 19 | extends JsObjectWrapper { 20 | /// User's display name. 21 | String get displayName => jsObject.displayName; 22 | void set displayName(String s) { 23 | jsObject.displayName = s; 24 | } 25 | 26 | /// User's e-mail address. 27 | String get email => jsObject.email; 28 | void set email(String s) { 29 | jsObject.email = s; 30 | } 31 | 32 | /// User's profile picture URL. 33 | String get photoURL => jsObject.photoURL; 34 | void set photoURL(String s) { 35 | jsObject.photoURL = s; 36 | } 37 | 38 | /// User's authentication provider ID. 39 | String get providerId => jsObject.providerId; 40 | void set providerId(String s) { 41 | jsObject.providerId = s; 42 | } 43 | 44 | /// User's unique ID. 45 | String get uid => jsObject.uid; 46 | void set uid(String s) { 47 | jsObject.uid = s; 48 | } 49 | 50 | /// Creates a new UserInfo from a [jsObject]. 51 | UserInfo.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 52 | } 53 | 54 | /// User account. 55 | /// 56 | /// See: . 57 | class User extends UserInfo { 58 | /// If the user's email address has been already verified. 59 | bool get emailVerified => jsObject.emailVerified; 60 | 61 | /// If the user is anonymous. 62 | bool get isAnonymous => jsObject.isAnonymous; 63 | 64 | /// List of additional provider-specific information about the user. 65 | List get providerData => jsObject.providerData 66 | .map((data) => 67 | new UserInfo.fromJsObject(data)) 68 | .toList(); 69 | 70 | /// Refresh token for the user account. 71 | String get refreshToken => jsObject.refreshToken; 72 | 73 | /// Creates a new User from a [jsObject]. 74 | User.fromJsObject(firebase_interop.UserJsImpl jsObject) 75 | : super.fromJsObject(jsObject); 76 | 77 | /// Deletes and signs out the user. 78 | Future delete() => handleThenable(jsObject.delete()); 79 | 80 | /// Returns a JWT token used to identify the user to a Firebase service. 81 | /// It forces refresh regardless of token expiration if [forceRefresh] 82 | /// parameter is [true]. 83 | Future getToken([bool forceRefresh = false]) => 84 | handleThenable(jsObject.getToken(forceRefresh)); 85 | 86 | /// Links the user account with the given [credential] and returns the user. 87 | Future link(AuthCredential credential) => handleThenableWithMapper( 88 | jsObject.link(credential), (u) => new User.fromJsObject(u)); 89 | 90 | /// Links the authenticated [provider] to the user account using 91 | /// a pop-up based OAuth flow. 92 | /// It returns the [UserCredential] information if linking is successful. 93 | Future linkWithPopup(AuthProvider provider) => 94 | handleThenableWithMapper(jsObject.linkWithPopup(provider.jsObject), 95 | (u) => new UserCredential.fromJsObject(u)); 96 | 97 | /// Links the authenticated [provider] to the user account using 98 | /// a full-page redirect flow. 99 | Future linkWithRedirect(AuthProvider provider) => 100 | handleThenable(jsObject.linkWithRedirect(provider.jsObject)); 101 | 102 | /// Re-authenticates a user using a fresh [credential]. Should be used 103 | /// before operations such as [updatePassword()] that require tokens 104 | /// from recent sign in attempts. 105 | Future reauthenticate(AuthCredential credential) => 106 | handleThenable(jsObject.reauthenticate(credential)); 107 | 108 | /// If signed in, it refreshes the current user. 109 | Future reload() => handleThenable(jsObject.reload()); 110 | 111 | /// Sends an e-mail verification to a user. 112 | Future sendEmailVerification() => 113 | handleThenable(jsObject.sendEmailVerification()); 114 | 115 | /// Unlinks a provider with [providerId] from a user account. 116 | Future unlink(String providerId) => handleThenableWithMapper( 117 | jsObject.unlink(providerId), 118 | (firebase_interop.UserJsImpl u) => new User.fromJsObject(u)); 119 | 120 | /// Updates the user's e-mail address to [newEmail]. 121 | Future updateEmail(String newEmail) => 122 | handleThenable(jsObject.updateEmail(newEmail)); 123 | 124 | /// Updates the user's password to [newPassword]. 125 | /// Requires the user to have recently signed in. If not, ask the user 126 | /// to authenticate again and then use [reauthenticate()]. 127 | Future updatePassword(String newPassword) => 128 | handleThenable(jsObject.updatePassword(newPassword)); 129 | 130 | /// Updates a user's [profile] data. 131 | /// UserProfile has a displayName and photoURL. 132 | /// 133 | /// UserProfile profile = new UserProfile(displayName: "Smart user"); 134 | /// await user.updateProfile(profile); 135 | Future updateProfile(firebase_interop.UserProfile profile) => 136 | handleThenable(jsObject.updateProfile(profile)); 137 | } 138 | 139 | /// The Firebase Auth service class. 140 | /// 141 | /// See: . 142 | class Auth extends JsObjectWrapper { 143 | App _app; 144 | 145 | /// App for this instance of auth service. 146 | App get app { 147 | if (_app != null) { 148 | _app.jsObject = jsObject.app; 149 | } else { 150 | _app = new App.fromJsObject(jsObject.app); 151 | } 152 | return _app; 153 | } 154 | 155 | User _currentUser; 156 | 157 | /// Currently signed-in user. 158 | User get currentUser { 159 | if (jsObject.currentUser != null) { 160 | if (_currentUser != null) { 161 | _currentUser.jsObject = jsObject.currentUser; 162 | } else { 163 | _currentUser = new User.fromJsObject(jsObject.currentUser); 164 | } 165 | } else { 166 | _currentUser = null; 167 | } 168 | return _currentUser; 169 | } 170 | 171 | var _onAuthUnsubscribe; 172 | StreamController _changeController; 173 | 174 | /// Stream for an auth state changed event. 175 | Stream get onAuthStateChanged { 176 | if (_changeController == null) { 177 | var nextWrapper = allowInterop((firebase_interop.UserJsImpl user) { 178 | _changeController.add( 179 | new AuthEvent((user != null) ? new User.fromJsObject(user) : null)); 180 | }); 181 | 182 | var errorWrapper = allowInterop((e) => _changeController.addError(e)); 183 | 184 | void startListen() { 185 | _onAuthUnsubscribe = 186 | jsObject.onAuthStateChanged(nextWrapper, errorWrapper); 187 | } 188 | 189 | void stopListen() { 190 | _onAuthUnsubscribe(); 191 | } 192 | 193 | _changeController = new StreamController.broadcast( 194 | onListen: startListen, onCancel: stopListen, sync: true); 195 | } 196 | return _changeController.stream; 197 | } 198 | 199 | /// Creates a new Auth from a [jsObject]. 200 | Auth.fromJsObject(AuthJsImpl jsObject) : super.fromJsObject(jsObject); 201 | 202 | /// Applies a verification [code] sent to the user by e-mail or by other 203 | /// out-of-band mechanism. 204 | Future applyActionCode(String code) => 205 | handleThenable(jsObject.applyActionCode(code)); 206 | 207 | /// Checks a verification [code] sent to the user by e-mail or by other 208 | /// out-of-band mechanism. 209 | /// It returns [ActionCodeInfo], metadata about the code. 210 | Future checkActionCode(String code) => 211 | handleThenable(jsObject.checkActionCode(code)); 212 | 213 | /// Completes password reset process with a [code] and a [newPassword]. 214 | Future confirmPasswordReset(String code, String newPassword) => 215 | handleThenable(jsObject.confirmPasswordReset(code, newPassword)); 216 | 217 | /// Creates a new user account with [email] and [password]. 218 | /// After a successful creation, the user will be signed into application 219 | /// and the [User] object is returned. 220 | /// 221 | /// The creation can fail, if the user with given [email] already exists 222 | /// or the password is not valid. 223 | Future createUserWithEmailAndPassword(String email, String password) => 224 | handleThenableWithMapper( 225 | jsObject.createUserWithEmailAndPassword(email, password), 226 | (u) => new User.fromJsObject(u)); 227 | 228 | /// Returns the list of provider IDs for the given [email] address, 229 | /// that can be used to sign in. 230 | Future> fetchProvidersForEmail(String email) => 231 | handleThenable(jsObject.fetchProvidersForEmail(email)); 232 | 233 | /// Returns a [UserCredential] from the redirect-based sign in flow. 234 | /// If sign is successful, returns the signed in user. Or fails with an error 235 | /// if sign is unsuccessful. 236 | /// The [UserCredential] with a null [User] is returned if no redirect 237 | /// operation was called. 238 | Future getRedirectResult() => handleThenableWithMapper( 239 | jsObject.getRedirectResult(), (u) => new UserCredential.fromJsObject(u)); 240 | 241 | /// Sends a password reset e-mail to the given [email]. 242 | /// To confirm password reset, use the [Auth.confirmPasswordReset]. 243 | Future sendPasswordResetEmail(String email) => 244 | handleThenable(jsObject.sendPasswordResetEmail(email)); 245 | 246 | /// Signs in as an anonymous user. If an anonymous user is already 247 | /// signed in, that user will be returned. In other case, new anonymous 248 | /// [User] identity is created and returned. 249 | Future signInAnonymously() => handleThenableWithMapper( 250 | jsObject.signInAnonymously(), (u) => new User.fromJsObject(u)); 251 | 252 | /// Signs in with the given [credential] and returns the [User]. 253 | Future signInWithCredential(AuthCredential credential) => 254 | handleThenableWithMapper(jsObject.signInWithCredential(credential), 255 | (u) => new User.fromJsObject(u)); 256 | 257 | /// Signs in with the custom [token] and returns the [User]. 258 | /// Custom token must be generated by an auth backend. 259 | /// Fails with an error if the token is invalid, expired or not accepted 260 | /// by Firebase Auth service. 261 | Future signInWithCustomToken(String token) => handleThenableWithMapper( 262 | jsObject.signInWithCustomToken(token), (u) => new User.fromJsObject(u)); 263 | 264 | /// Signs in with [email] and [password] and returns the [User]. 265 | /// Fails with an error if the sign in is not successful. 266 | Future signInWithEmailAndPassword(String email, String password) => 267 | handleThenableWithMapper( 268 | jsObject.signInWithEmailAndPassword(email, password), 269 | (u) => new User.fromJsObject(u)); 270 | 271 | /// Signs in using a popup-based OAuth authentication flow with the 272 | /// given [provider]. 273 | /// Returns [UserCredential] if successful, or an error object if unsuccessful. 274 | Future signInWithPopup(AuthProvider provider) => 275 | handleThenableWithMapper(jsObject.signInWithPopup(provider.jsObject), 276 | (u) => new UserCredential.fromJsObject(u)); 277 | 278 | /// Signs in using a full-page redirect flow with the given [provider]. 279 | Future signInWithRedirect(AuthProvider provider) => 280 | handleThenable(jsObject.signInWithRedirect(provider.jsObject)); 281 | 282 | /// Signs out the current user. 283 | Future signOut() => handleThenable(jsObject.signOut()); 284 | 285 | /// Verifies a password reset [code] sent to the user by email 286 | /// or other out-of-band mechanism. 287 | /// Returns the user's e-mail address if valid. 288 | Future verifyPasswordResetCode(String code) => 289 | handleThenable(jsObject.verifyPasswordResetCode(code)); 290 | } 291 | 292 | /// Represents an auth provider. 293 | /// 294 | /// See: . 295 | abstract class AuthProvider 296 | extends JsObjectWrapper { 297 | /// Provider id. 298 | String get providerId => jsObject.providerId; 299 | 300 | /// Creates a new AuthProvider from a [jsObject]. 301 | AuthProvider.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 302 | } 303 | 304 | /// E-mail and password auth provider implementation. 305 | /// 306 | /// See: . 307 | class EmailAuthProvider extends AuthProvider { 308 | static String PROVIDER_ID = EmailAuthProviderJsImpl.PROVIDER_ID; 309 | 310 | /// Creates a new EmailAuthProvider. 311 | factory EmailAuthProvider() => 312 | new EmailAuthProvider.fromJsObject(new EmailAuthProviderJsImpl()); 313 | 314 | /// Creates a new EmailAuthProvider from a [jsObject]. 315 | EmailAuthProvider.fromJsObject(EmailAuthProviderJsImpl jsObject) 316 | : super.fromJsObject(jsObject); 317 | 318 | /// Creates a credential for e-mail. 319 | static AuthCredential credential(String email, String password) => 320 | EmailAuthProviderJsImpl.credential(email, password); 321 | } 322 | 323 | /// Facebook auth provider. 324 | /// 325 | /// See: . 326 | class FacebookAuthProvider extends AuthProvider { 327 | static String PROVIDER_ID = FacebookAuthProviderJsImpl.PROVIDER_ID; 328 | 329 | /// Creates a new FacebookAuthProvider. 330 | factory FacebookAuthProvider() => 331 | new FacebookAuthProvider.fromJsObject(new FacebookAuthProviderJsImpl()); 332 | 333 | /// Creates a new FacebookAuthProvider from a [jsObject]. 334 | FacebookAuthProvider.fromJsObject(FacebookAuthProviderJsImpl jsObject) 335 | : super.fromJsObject(jsObject); 336 | 337 | void addScope(String scope) => jsObject.addScope(scope); 338 | 339 | /// Sets the OAuth custom parameters to pass in a Facebook OAuth request 340 | /// for popup and redirect sign-in operations. 341 | /// Valid parameters include 'auth_type', 'display' and 'locale'. 342 | /// For a detailed list, check the Facebook documentation. 343 | /// Reserved required OAuth 2.0 parameters such as 'client_id', 344 | /// 'redirect_uri', 'scope', 'response_type' and 'state' are not allowed 345 | /// and ignored. 346 | void setCustomParameters(Map customOAuthParameters) => 347 | jsObject.setCustomParameters(jsify(customOAuthParameters)); 348 | 349 | /// Creates a credential for Facebook. 350 | static AuthCredential credential(String token) => 351 | FacebookAuthProviderJsImpl.credential(token); 352 | } 353 | 354 | /// Github auth provider. 355 | /// 356 | /// See: . 357 | class GithubAuthProvider extends AuthProvider { 358 | static String PROVIDER_ID = GithubAuthProviderJsImpl.PROVIDER_ID; 359 | 360 | /// Creates a new GithubAuthProvider. 361 | factory GithubAuthProvider() => 362 | new GithubAuthProvider.fromJsObject(new GithubAuthProviderJsImpl()); 363 | 364 | /// Creates a new GithubAuthProvider from a [jsObject]. 365 | GithubAuthProvider.fromJsObject(GithubAuthProviderJsImpl jsObject) 366 | : super.fromJsObject(jsObject); 367 | 368 | void addScope(String scope) => jsObject.addScope(scope); 369 | 370 | /// Sets the OAuth custom parameters to pass in a GitHub OAuth request 371 | /// for popup and redirect sign-in operations. 372 | /// Valid parameters include 'allow_signup'. 373 | /// For a detailed list, check the GitHub documentation. 374 | /// Reserved required OAuth 2.0 parameters such as 'client_id', 375 | /// 'redirect_uri', 'scope', 'response_type' and 'state' 376 | /// are not allowed and ignored. 377 | void setCustomParameters(Map customOAuthParameters) => 378 | jsObject.setCustomParameters(jsify(customOAuthParameters)); 379 | 380 | /// Creates a credential for Github. 381 | static AuthCredential credential(String token) => 382 | GithubAuthProviderJsImpl.credential(token); 383 | } 384 | 385 | /// Google auth provider. 386 | /// 387 | /// See: . 388 | class GoogleAuthProvider extends AuthProvider { 389 | static String PROVIDER_ID = GoogleAuthProviderJsImpl.PROVIDER_ID; 390 | 391 | /// Creates a new GoogleAuthProvider. 392 | factory GoogleAuthProvider() => 393 | new GoogleAuthProvider.fromJsObject(new GoogleAuthProviderJsImpl()); 394 | 395 | /// Creates a new GoogleAuthProvider from a [jsObject]. 396 | GoogleAuthProvider.fromJsObject(GoogleAuthProviderJsImpl jsObject) 397 | : super.fromJsObject(jsObject); 398 | 399 | void addScope(String scope) => jsObject.addScope(scope); 400 | 401 | /// Sets the OAuth custom parameters to pass in a Google OAuth request 402 | /// for popup and redirect sign-in operations. 403 | /// Valid parameters include 'hd', 'hl', 'include_granted_scopes', 404 | /// 'login_hint' and 'prompt'. 405 | /// For a detailed list, check the Google documentation. 406 | /// Reserved required OAuth 2.0 parameters such as 'client_id', 407 | /// 'redirect_uri', 'scope', 'response_type' and 'state' 408 | /// are not allowed and ignored. 409 | void setCustomParameters(Map customOAuthParameters) => 410 | jsObject.setCustomParameters(jsify(customOAuthParameters)); 411 | 412 | /// Creates a credential for Google. 413 | /// At least one of [idToken] and [accessToken] is required. 414 | static AuthCredential credential([String idToken, String accessToken]) => 415 | GoogleAuthProviderJsImpl.credential(idToken, accessToken); 416 | } 417 | 418 | /// Twitter auth provider. 419 | /// 420 | /// See: . 421 | class TwitterAuthProvider extends AuthProvider { 422 | static String PROVIDER_ID = TwitterAuthProviderJsImpl.PROVIDER_ID; 423 | 424 | /// Creates a new TwitterAuthProvider. 425 | factory TwitterAuthProvider() => 426 | new TwitterAuthProvider.fromJsObject(new TwitterAuthProviderJsImpl()); 427 | 428 | /// Creates a new TwitterAuthProvider from a [jsObject]. 429 | TwitterAuthProvider.fromJsObject(TwitterAuthProviderJsImpl jsObject) 430 | : super.fromJsObject(jsObject); 431 | 432 | /// Sets the OAuth custom parameters to pass in a Twitter OAuth request 433 | /// for popup and redirect sign-in operations. 434 | /// Valid parameters include 'lang'. Reserved required OAuth 1.0 parameters 435 | /// such as 'oauth_consumer_key', 'oauth_token', 'oauth_signature', etc 436 | /// are not allowed and will be ignored. 437 | void setCustomParameters(Map customOAuthParameters) => 438 | jsObject.setCustomParameters(jsify(customOAuthParameters)); 439 | 440 | /// Creates a credential for Twitter. 441 | static AuthCredential credential(String token, String secret) => 442 | TwitterAuthProviderJsImpl.credential(token, secret); 443 | } 444 | 445 | /// Event propagated in Stream controllers when an auth state changes. 446 | class AuthEvent { 447 | /// The user. 448 | final User user; 449 | 450 | /// Creates a new AuthEvent with user. 451 | AuthEvent(this.user); 452 | } 453 | 454 | /// A structure containing [User] and [AuthCredential]. 455 | class UserCredential extends JsObjectWrapper { 456 | User _user; 457 | 458 | /// Returns the user. 459 | User get user { 460 | if (jsObject.user != null) { 461 | if (_user != null) { 462 | _user.jsObject = jsObject.user; 463 | } else { 464 | _user = new User.fromJsObject(jsObject.user); 465 | } 466 | } else { 467 | _user = null; 468 | } 469 | return _user; 470 | } 471 | 472 | /// Sets the user to [u]. 473 | void set user(User u) { 474 | _user = u; 475 | jsObject.user = u.jsObject; 476 | } 477 | 478 | /// Returns the auth credential. 479 | AuthCredential get credential => jsObject.credential; 480 | 481 | /// Sets the auth credential to [c]. 482 | void set credential(AuthCredential c) { 483 | jsObject.credential = c; 484 | } 485 | 486 | /// Creates a new UserCredential with optional [user] and [credential]. 487 | factory UserCredential({User user, AuthCredential credential}) => 488 | new UserCredential.fromJsObject(new UserCredentialJsImpl( 489 | user: user.jsObject, credential: credential)); 490 | 491 | /// Creates a new UserCredential from a [jsObject]. 492 | UserCredential.fromJsObject(UserCredentialJsImpl jsObject) 493 | : super.fromJsObject(jsObject); 494 | } 495 | -------------------------------------------------------------------------------- /lib/src/database.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:func/func.dart'; 4 | import 'package:js/js.dart'; 5 | 6 | import 'app.dart'; 7 | import 'interop/database_interop.dart' as database_interop; 8 | import 'js.dart'; 9 | import 'utils.dart'; 10 | 11 | export 'interop/database_interop.dart' show ServerValue; 12 | 13 | /// Logs debugging information to the console. 14 | /// If [persistent], it remembers the logging state between page refreshes. 15 | /// 16 | /// See: . 17 | void enableLogging([logger, bool persistent = false]) => 18 | database_interop.enableLogging(jsify(logger), persistent); 19 | 20 | /// Firebase realtime database service class. 21 | /// 22 | /// See: . 23 | class Database extends JsObjectWrapper { 24 | App _app; 25 | 26 | /// App for this instance of database service. 27 | App get app { 28 | if (_app != null) { 29 | _app.jsObject = jsObject.app; 30 | } else { 31 | _app = new App.fromJsObject(jsObject.app); 32 | } 33 | return _app; 34 | } 35 | 36 | /// Creates a new Database from a [jsObject]. 37 | Database.fromJsObject(database_interop.DatabaseJsImpl jsObject) 38 | : super.fromJsObject(jsObject); 39 | 40 | /// Disconnects from the server, all database operations will be 41 | /// completed offline. 42 | void goOffline() => jsObject.goOffline(); 43 | 44 | /// Connects to the server and synchronizes the offline database 45 | /// state with the server state. 46 | void goOnline() => jsObject.goOnline(); 47 | 48 | /// Returns a [DatabaseReference] to the root or provided [path]. 49 | DatabaseReference ref([String path]) => 50 | new DatabaseReference.fromJsObject(jsObject.ref(path)); 51 | 52 | /// Returns a [DatabaseReference] from provided [url]. 53 | /// Url must be in the same domain as the current database. 54 | DatabaseReference refFromURL(String url) => 55 | new DatabaseReference.fromJsObject(jsObject.refFromURL(url)); 56 | } 57 | 58 | /// A DatabaseReference represents a specific location in database and 59 | /// can be used for reading or writing data to that database location. 60 | /// 61 | /// See: . 62 | class DatabaseReference 63 | extends Query { 64 | /// The last part of the current path. 65 | /// It is [null] in case of root DatabaseReference. 66 | String get key => jsObject.key; 67 | 68 | DatabaseReference _parent; 69 | 70 | /// The parent location of a DatabaseReference. 71 | DatabaseReference get parent { 72 | if (jsObject.parent != null) { 73 | if (_parent != null) { 74 | _parent.jsObject = jsObject.parent; 75 | } else { 76 | _parent = new DatabaseReference.fromJsObject(jsObject.parent); 77 | } 78 | } else { 79 | _parent = null; 80 | } 81 | return _parent; 82 | } 83 | 84 | DatabaseReference _root; 85 | 86 | /// The root location of a DatabaseReference. 87 | DatabaseReference get root { 88 | if (_root != null) { 89 | _root.jsObject = jsObject.root; 90 | } else { 91 | _root = new DatabaseReference.fromJsObject(jsObject.root); 92 | } 93 | return _root; 94 | } 95 | 96 | /// Creates a new DatabaseReference from a [jsObject]. 97 | DatabaseReference.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 98 | 99 | /// Returns child DatabaseReference from provided relative [path]. 100 | DatabaseReference child(String path) => 101 | new DatabaseReference.fromJsObject(jsObject.child(path)); 102 | 103 | /// Returns [OnDisconnect] object. 104 | OnDisconnect onDisconnect() => 105 | new OnDisconnect.fromJsObject(jsObject.onDisconnect()); 106 | 107 | /// Pushes provided [value] to the actual location. 108 | /// 109 | /// The [value] must be a Dart basic type or the error is thrown. 110 | /// 111 | /// If the [value] is not provided, no data is written to the database 112 | /// but the [ThenableReference] is still returned and can be used for later 113 | /// operation. 114 | /// 115 | /// DatabaseReference ref = firebase.database().ref("messages"); 116 | /// ThenableReference childRef = ref.push(); 117 | /// childRef.set({"text": "Hello"}); 118 | /// 119 | /// This method returns [ThenableReference], [DatabaseReference] 120 | /// with a [Future] property. 121 | ThenableReference push([value]) => 122 | new ThenableReference.fromJsObject(jsObject.push(jsify(value))); 123 | 124 | /// Removes data from actual database location. 125 | Future remove() => handleThenable(jsObject.remove()); 126 | 127 | /// Sets data at actual database location to provided [value]. 128 | /// Overwrites any existing data at actual location and all child locations. 129 | /// 130 | /// The [value] must be a Dart basic type or the error is thrown. 131 | Future set(value) => handleThenable(jsObject.set(jsify(value))); 132 | 133 | /// Sets a priority for data at actual database location. 134 | /// 135 | /// The [priority] must be a [String], [num] or [null], or the error is thrown. 136 | Future setPriority(priority) => 137 | handleThenable(jsObject.setPriority(priority)); 138 | 139 | /// Sets data [newVal] at actual database location with provided priority 140 | /// [newPriority]. 141 | /// 142 | /// Like [set()] but also specifies the priority. 143 | /// 144 | /// The [newVal] must be a Dart basic type or the error is thrown. 145 | /// The [newPriority] must be a [String], [num] or [null], or the error 146 | /// is thrown. 147 | Future setWithPriority(newVal, newPriority) => 148 | handleThenable(jsObject.setWithPriority(jsify(newVal), newPriority)); 149 | 150 | /// Atomically updates data at actual database location. 151 | /// 152 | /// This method is used to update the existing value to a new value, 153 | /// ensuring there are no conflicts with other clients writing to the same 154 | /// location at the same time. 155 | /// 156 | /// The provided [transactionUpdate] function is used to update 157 | /// the current value into a new value. 158 | /// 159 | /// DatabaseReference ref = firebase.database().ref("numbers"); 160 | /// ref.set(2); 161 | /// ref.transaction((currentValue) => currentValue * 2); 162 | /// 163 | /// var event = await ref.once("value"); 164 | /// print(event.snapshot.val()); //prints 4 165 | /// 166 | /// The returned value from [transactionUpdate] function must be a Dart basic 167 | /// type or the error is thrown. 168 | /// 169 | /// Set [applyLocally] to [false] to not see intermediate states. 170 | Future transaction(Func1 transactionUpdate, 171 | [bool applyLocally = true]) { 172 | Completer c = new Completer(); 173 | 174 | var transactionUpdateWrap = 175 | allowInterop((update) => jsify(transactionUpdate(dartify(update)))); 176 | 177 | var onCompleteWrap = allowInterop( 178 | (error, bool committed, database_interop.DataSnapshotJsImpl snapshot) { 179 | var dataSnapshot = 180 | (snapshot != null) ? new DataSnapshot.fromJsObject(snapshot) : null; 181 | if (error != null) { 182 | c.completeError(error); 183 | } else { 184 | c.complete( 185 | new Transaction(committed: committed, snapshot: dataSnapshot)); 186 | } 187 | }); 188 | 189 | jsObject.transaction(transactionUpdateWrap, onCompleteWrap, applyLocally); 190 | return c.future; 191 | } 192 | 193 | /// Updates data with [values] at actual database location. 194 | /// 195 | /// The [values] must be a Dart basic type or the error is thrown. 196 | Future update(values) => handleThenable(jsObject.update(jsify(values))); 197 | } 198 | 199 | /// Event fired when data changes at location. 200 | /// 201 | /// Example: 202 | /// 203 | /// Database database = firebase.database(); 204 | /// database.ref("messages").onValue.listen((QueryEvent e) { 205 | /// DataSnapshot datasnapshot = e.snapshot; 206 | /// //... 207 | /// }); 208 | class QueryEvent { 209 | /// Immutable copy of the data at a database location. 210 | final DataSnapshot snapshot; 211 | 212 | /// String containing the key of the previous child. 213 | final String prevChildKey; 214 | 215 | /// Creates a new QueryEvent with [snapshot] and optional [prevChildKey]. 216 | QueryEvent(this.snapshot, [this.prevChildKey]); 217 | } 218 | 219 | /// A Query sorts and filters the data at a database location so only 220 | /// a subset of the child data is included. This can be used to order 221 | /// a collection of data by some attribute as well as to restrict 222 | /// a large list of items down to a number suitable for synchronizing 223 | /// to the client. 224 | /// 225 | /// Queries are created by chaining together one or more of the filter 226 | /// methods defined in this class. 227 | /// 228 | /// See: . 229 | class Query extends JsObjectWrapper { 230 | DatabaseReference _ref; 231 | 232 | /// DatabaseReference to the Query's location. 233 | DatabaseReference get ref { 234 | if (_ref != null) { 235 | _ref.jsObject = jsObject.ref; 236 | } else { 237 | _ref = new DatabaseReference.fromJsObject(jsObject.ref); 238 | } 239 | return _ref; 240 | } 241 | 242 | Stream _onValue; 243 | 244 | /// Stream for a value event. Event is triggered once with the initial 245 | /// data stored at location, and then again each time the data changes. 246 | Stream get onValue => _onValue ??= _createStream("value"); 247 | 248 | Stream _onChildAdded; 249 | 250 | /// Stream for a child_added event. Event is triggered once for each 251 | /// initial child at location, and then again every time a new child is added. 252 | Stream get onChildAdded => 253 | _onChildAdded ??= _createStream("child_added"); 254 | 255 | Stream _onChildRemoved; 256 | 257 | /// Stream for a child_removed event. Event is triggered once every time 258 | /// a child is removed. 259 | Stream get onChildRemoved => 260 | _onChildRemoved ??= _createStream("child_removed"); 261 | 262 | Stream _onChildChanged; 263 | 264 | /// Stream for a child_changed event. Event is triggered when the data 265 | /// stored in a child (or any of its descendants) changes. 266 | /// Single child_changed event may represent multiple changes to the child. 267 | Stream get onChildChanged => 268 | _onChildChanged ??= _createStream("child_changed"); 269 | 270 | Stream _onChildMoved; 271 | 272 | /// Stream for a child_moved event. Event is triggered when a child's priority 273 | /// changes such that its position relative to its siblings changes. 274 | Stream get onChildMoved => 275 | _onChildMoved ??= _createStream("child_moved"); 276 | 277 | /// Creates a new Query from a [jsObject]. 278 | Query.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 279 | 280 | /// Returns a Query with the ending point [value]. The ending point 281 | /// is inclusive. 282 | /// 283 | /// The [value] must be a [num], [String], [bool], or [null], or the error 284 | /// is thrown. 285 | /// The optional [key] can be used to further limit the range of the query. 286 | Query endAt(value, [String key]) => new Query.fromJsObject( 287 | key == null ? jsObject.endAt(value) : jsObject.endAt(value, key)); 288 | 289 | /// Returns a Query which includes children which match the specified [value]. 290 | /// 291 | /// The [value] must be a [num], [String], [bool], or [null], or the error 292 | /// is thrown. 293 | /// The optional [key] can be used to further limit the range of the query. 294 | Query equalTo(value, [String key]) => new Query.fromJsObject( 295 | key == null ? jsObject.equalTo(value) : jsObject.equalTo(value, key)); 296 | 297 | /// Returns [true] if the current and [other] queries are equal - they 298 | /// represent the exactly same location, have the same query parameters, 299 | /// and are from the same instance of [App]. 300 | /// Equivalent queries share the same sort order, limits, starting 301 | /// and ending points. 302 | /// 303 | /// Two [DatabaseReference] objects are equivalent if they represent the same 304 | /// location and are from the same instance of [App]. 305 | bool isEqual(Query other) => jsObject.isEqual(other.jsObject); 306 | 307 | /// Returns a new Query limited to the first specific number of children 308 | /// provided by [limit]. 309 | Query limitToFirst(int limit) => 310 | new Query.fromJsObject(jsObject.limitToFirst(limit)); 311 | 312 | /// Returns a new Query limited to the last specific number of children 313 | /// provided by [limit]. 314 | Query limitToLast(int limit) => 315 | new Query.fromJsObject(jsObject.limitToLast(limit)); 316 | 317 | Stream _createStream(String eventType) { 318 | StreamController streamController; 319 | 320 | var callbackWrap = allowInterop((database_interop.DataSnapshotJsImpl data, 321 | [String string]) { 322 | streamController 323 | .add(new QueryEvent(new DataSnapshot.fromJsObject(data), string)); 324 | }); 325 | 326 | void startListen() { 327 | jsObject.on(eventType, callbackWrap); 328 | } 329 | 330 | void stopListen() { 331 | jsObject.off(eventType); 332 | } 333 | 334 | streamController = new StreamController.broadcast( 335 | onListen: startListen, onCancel: stopListen, sync: true); 336 | return streamController.stream; 337 | } 338 | 339 | /// Listens for exactly one [eventType] and then stops listening. 340 | Future once(String eventType) { 341 | Completer c = new Completer(); 342 | 343 | jsObject.once(eventType, allowInterop( 344 | (database_interop.DataSnapshotJsImpl snapshot, [String string]) { 345 | c.complete( 346 | new QueryEvent(new DataSnapshot.fromJsObject(snapshot), string)); 347 | }), resolveError(c)); 348 | 349 | return c.future; 350 | } 351 | 352 | /// Returns a new Query ordered by the specified child [path]. 353 | Query orderByChild(String path) => 354 | new Query.fromJsObject(jsObject.orderByChild(path)); 355 | 356 | /// Returns a new Query ordered by key. 357 | Query orderByKey() => new Query.fromJsObject(jsObject.orderByKey()); 358 | 359 | /// Returns a new Query ordered by priority. 360 | Query orderByPriority() => new Query.fromJsObject(jsObject.orderByPriority()); 361 | 362 | /// Returns a new Query ordered by child values. 363 | Query orderByValue() => new Query.fromJsObject(jsObject.orderByValue()); 364 | 365 | /// Returns a Query with the starting point [value]. The starting point 366 | /// is inclusive. 367 | /// 368 | /// The [value] must be a [num], [String], [bool], or [null], or the error 369 | /// is thrown. 370 | /// The optional [key] can be used to further limit the range of the query. 371 | Query startAt(value, [String key]) => new Query.fromJsObject( 372 | key == null ? jsObject.startAt(value) : jsObject.startAt(value, key)); 373 | 374 | /// Returns a String representation of Query object. 375 | @override 376 | String toString() => jsObject.toString(); 377 | } 378 | 379 | /// A DataSnapshot contains data from a database location. 380 | /// 381 | /// See: . 382 | class DataSnapshot 383 | extends JsObjectWrapper { 384 | /// The last part of the path at location for this DataSnapshot. 385 | String get key => jsObject.key; 386 | 387 | DatabaseReference _ref; 388 | 389 | /// The DatabaseReference for the location that generated this DataSnapshot. 390 | DatabaseReference get ref { 391 | if (_ref != null) { 392 | _ref.jsObject = jsObject.ref; 393 | } else { 394 | _ref = new DatabaseReference.fromJsObject(jsObject.ref); 395 | } 396 | return _ref; 397 | } 398 | 399 | /// Creates a new DataSnapshot from a [jsObject]. 400 | DataSnapshot.fromJsObject(database_interop.DataSnapshotJsImpl jsObject) 401 | : super.fromJsObject(jsObject); 402 | 403 | /// Returns DataSnapshot for the location at the specified relative [path]. 404 | DataSnapshot child(String path) => 405 | new DataSnapshot.fromJsObject(jsObject.child(path)); 406 | 407 | /// Returns [true] if this DataSnapshot contains any data. 408 | bool exists() => jsObject.exists(); 409 | 410 | /// Exports the contents of the DataSnapshot as a Dart object. 411 | dynamic exportVal() => dartify(jsObject.exportVal()); 412 | 413 | /// Enumerates the top-level children of the DataSnapshot in their query-order. 414 | /// [action] is called for each child DataSnapshot. 415 | bool forEach(Func1 action) { 416 | var actionWrap = allowInterop((database_interop.DataSnapshotJsImpl data) { 417 | action(new DataSnapshot.fromJsObject(data)); 418 | }); 419 | return jsObject.forEach(actionWrap); 420 | } 421 | 422 | /// Returns priority for data in this DataSnapshot. 423 | dynamic getPriority() => jsObject.getPriority(); 424 | 425 | /// Returns [true] if the specified child [path] has data. 426 | bool hasChild(String path) => jsObject.hasChild(path); 427 | 428 | /// Returns [true] if this DataSnapshot has any children. 429 | bool hasChildren() => jsObject.hasChildren(); 430 | 431 | /// Returns the number of child properties for this DataSnapshot. 432 | int numChildren() => jsObject.numChildren(); 433 | 434 | /// Returns Dart value from a DataSnapshot. 435 | dynamic val() => dartify(jsObject.val()); 436 | } 437 | 438 | /// The OnDisconnect class allows you to write or clear data when your client 439 | /// disconnects from the database server. 440 | /// 441 | /// See: . 442 | class OnDisconnect 443 | extends JsObjectWrapper { 444 | OnDisconnect.fromJsObject(database_interop.OnDisconnectJsImpl jsObject) 445 | : super.fromJsObject(jsObject); 446 | 447 | /// Cancels all previously queued onDisconnect() events for actual location 448 | /// and all children. 449 | Future cancel() => handleThenable(jsObject.cancel()); 450 | 451 | /// Ensures the data for actual location is deleted when the client 452 | /// is disconnected. 453 | Future remove() => handleThenable(jsObject.remove()); 454 | 455 | /// Ensures the data for actual location is set to the specified [value] 456 | /// when the client is disconnected. 457 | /// 458 | /// The [value] must be a Dart basic type or the error is thrown. 459 | Future set(value) => handleThenable(jsObject.set(jsify(value))); 460 | 461 | /// Ensures the data for actual location is set to the specified [value] 462 | /// and [priority] when the client is disconnected. 463 | /// 464 | /// The [value] must be a Dart basic type or the error is thrown. 465 | /// The [priority] must be a [String], [num] or [null], or the error is thrown. 466 | Future setWithPriority(value, priority) => 467 | handleThenable(jsObject.setWithPriority(jsify(value), priority)); 468 | 469 | /// Writes multiple [values] at actual location when the client is disconnected. 470 | /// 471 | /// The [values] must be a Dart basic type or the error is thrown. 472 | Future update(values) => handleThenable(jsObject.update(jsify(values))); 473 | } 474 | 475 | /// The ThenableReference class represents [DatabaseReference] with a 476 | /// [Future] property. 477 | /// 478 | /// See: . 479 | class ThenableReference 480 | extends DatabaseReference { 481 | Future _future; 482 | 483 | /// Creates a new ThenableReference from a [jsObject]. 484 | ThenableReference.fromJsObject( 485 | database_interop.ThenableReferenceJsImpl jsObject) 486 | : super.fromJsObject(jsObject); 487 | 488 | /// A Future property. 489 | Future get future => _future ??= handleThenableWithMapper( 490 | jsObject, 491 | (database_interop.ReferenceJsImpl val) => 492 | new DatabaseReference.fromJsObject(val)); 493 | } 494 | 495 | /// A structure used in [DatabaseReference.transaction]. 496 | class Transaction extends JsObjectWrapper { 497 | /// If transaction was committed. 498 | bool get committed => jsObject.committed; 499 | 500 | /// Sets committed to [c]. 501 | void set committed(bool c) { 502 | jsObject.committed = c; 503 | } 504 | 505 | DataSnapshot _snapshot; 506 | 507 | /// Returns the DataSnapshot. 508 | DataSnapshot get snapshot { 509 | if (jsObject.snapshot != null) { 510 | if (_snapshot != null) { 511 | _snapshot.jsObject = jsObject.snapshot; 512 | } else { 513 | _snapshot = new DataSnapshot.fromJsObject(jsObject.snapshot); 514 | } 515 | } else { 516 | _snapshot = null; 517 | } 518 | return _snapshot; 519 | } 520 | 521 | /// Sets the DataSnapshot to [s]. 522 | void set snapshot(DataSnapshot s) { 523 | _snapshot = s; 524 | jsObject.snapshot = s.jsObject; 525 | } 526 | 527 | /// Creates a new Transaction with optional [committed] and [snapshot] 528 | /// properties. 529 | factory Transaction({bool committed, DataSnapshot snapshot}) => 530 | new Transaction.fromJsObject(new database_interop.TransactionJsImpl( 531 | committed: committed, snapshot: snapshot.jsObject)); 532 | 533 | /// Creates a new Transaction from a [jsObject]. 534 | Transaction.fromJsObject(database_interop.TransactionJsImpl jsObject) 535 | : super.fromJsObject(jsObject); 536 | } 537 | -------------------------------------------------------------------------------- /lib/src/interop/app_interop.dart: -------------------------------------------------------------------------------- 1 | @JS('firebase.app') 2 | library firebase3.app_interop; 3 | 4 | import 'package:js/js.dart'; 5 | 6 | import 'auth_interop.dart'; 7 | import 'database_interop.dart'; 8 | import 'firebase_interop.dart'; 9 | import 'storage_interop.dart'; 10 | 11 | @JS('App') 12 | abstract class AppJsImpl { 13 | external String get name; 14 | external FirebaseOptions get options; 15 | external AuthJsImpl auth(); 16 | external DatabaseJsImpl database(); 17 | external PromiseJsImpl delete(); 18 | external StorageJsImpl storage(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/interop/auth_interop.dart: -------------------------------------------------------------------------------- 1 | @JS('firebase.auth') 2 | library firebase3.auth_interop; 3 | 4 | import 'package:func/func.dart'; 5 | import 'package:js/js.dart'; 6 | 7 | import 'app_interop.dart'; 8 | import 'firebase_interop.dart'; 9 | 10 | @JS('Auth') 11 | abstract class AuthJsImpl { 12 | external AppJsImpl get app; 13 | external void set app(AppJsImpl a); 14 | external UserJsImpl get currentUser; 15 | external void set currentUser(UserJsImpl u); 16 | external PromiseJsImpl applyActionCode(String code); 17 | external PromiseJsImpl checkActionCode(String code); 18 | external PromiseJsImpl confirmPasswordReset(String code, String newPassword); 19 | external PromiseJsImpl createUserWithEmailAndPassword( 20 | String email, String password); 21 | external PromiseJsImpl> fetchProvidersForEmail(String email); 22 | external PromiseJsImpl getRedirectResult(); 23 | external Func0 onAuthStateChanged(nextOrObserver, 24 | [Func1 opt_error, Func0 opt_completed]); 25 | external PromiseJsImpl sendPasswordResetEmail(String email); 26 | external PromiseJsImpl signInAnonymously(); 27 | external PromiseJsImpl signInWithCredential( 28 | AuthCredential credential); 29 | external PromiseJsImpl signInWithCustomToken(String token); 30 | external PromiseJsImpl signInWithEmailAndPassword( 31 | String email, String password); 32 | external PromiseJsImpl signInWithPopup( 33 | AuthProviderJsImpl provider); 34 | external PromiseJsImpl signInWithRedirect(AuthProviderJsImpl provider); 35 | external PromiseJsImpl signOut(); 36 | external PromiseJsImpl verifyPasswordResetCode(String code); 37 | } 38 | 39 | /// Represents the credentials returned by an auth provider. 40 | /// Implementations specify the details about each auth provider's credential 41 | /// requirements. 42 | /// 43 | /// See: . 44 | @JS() 45 | abstract class AuthCredential { 46 | external String get provider; 47 | external String get accessToken; 48 | external String get secret; 49 | } 50 | 51 | @JS('AuthProvider') 52 | abstract class AuthProviderJsImpl { 53 | external String get providerId; 54 | } 55 | 56 | @JS('EmailAuthProvider') 57 | class EmailAuthProviderJsImpl extends AuthProviderJsImpl { 58 | external factory EmailAuthProviderJsImpl(); 59 | external static String get PROVIDER_ID; 60 | external static AuthCredential credential(String email, String password); 61 | } 62 | 63 | @JS('FacebookAuthProvider') 64 | class FacebookAuthProviderJsImpl extends AuthProviderJsImpl { 65 | external factory FacebookAuthProviderJsImpl(); 66 | external static String get PROVIDER_ID; 67 | external void addScope(String scope); 68 | external void setCustomParameters(customOAuthParameters); 69 | external static AuthCredential credential(String token); 70 | } 71 | 72 | @JS('GithubAuthProvider') 73 | class GithubAuthProviderJsImpl extends AuthProviderJsImpl { 74 | external factory GithubAuthProviderJsImpl(); 75 | external static String get PROVIDER_ID; 76 | external void addScope(String scope); 77 | external void setCustomParameters(customOAuthParameters); 78 | external static AuthCredential credential(String token); 79 | } 80 | 81 | @JS('GoogleAuthProvider') 82 | class GoogleAuthProviderJsImpl extends AuthProviderJsImpl { 83 | external factory GoogleAuthProviderJsImpl(); 84 | external static String get PROVIDER_ID; 85 | external void addScope(String scope); 86 | external void setCustomParameters(customOAuthParameters); 87 | external static AuthCredential credential( 88 | [String idToken, String accessToken]); 89 | } 90 | 91 | @JS('TwitterAuthProvider') 92 | class TwitterAuthProviderJsImpl extends AuthProviderJsImpl { 93 | external factory TwitterAuthProviderJsImpl(); 94 | external static String get PROVIDER_ID; 95 | external void setCustomParameters(customOAuthParameters); 96 | external static AuthCredential credential(String token, String secret); 97 | } 98 | 99 | /// A response from [Auth.checkActionCode]. 100 | /// 101 | /// See: . 102 | @JS() 103 | abstract class ActionCodeInfo { 104 | external ActionCodeEmail get data; 105 | } 106 | 107 | /// An authentication error. 108 | /// 109 | /// See: . 110 | @JS('Error') 111 | abstract class AuthError { 112 | external String get code; 113 | external void set code(String s); 114 | external String get message; 115 | external void set message(String s); 116 | } 117 | 118 | @JS() 119 | @anonymous 120 | class ActionCodeEmail { 121 | external String get email; 122 | } 123 | 124 | @JS() 125 | @anonymous 126 | class UserCredentialJsImpl { 127 | external UserJsImpl get user; 128 | external void set user(UserJsImpl u); 129 | external AuthCredential get credential; 130 | external void set credential(AuthCredential c); 131 | 132 | external factory UserCredentialJsImpl( 133 | {UserJsImpl user, AuthCredential credential}); 134 | } 135 | -------------------------------------------------------------------------------- /lib/src/interop/database_interop.dart: -------------------------------------------------------------------------------- 1 | @JS('firebase.database') 2 | library firebase3.database_interop; 3 | 4 | import 'package:func/func.dart'; 5 | import 'package:js/js.dart'; 6 | 7 | import 'app_interop.dart'; 8 | import 'firebase_interop.dart'; 9 | 10 | external void enableLogging([logger, bool persistent]); 11 | 12 | /// A placeholder value for auto-populating the current timestamp 13 | /// (time since the Unix epoch, in milliseconds) as determined 14 | /// by the Firebase servers. 15 | /// 16 | /// See: . 17 | @JS() 18 | abstract class ServerValue { 19 | external static Object get TIMESTAMP; 20 | } 21 | 22 | @JS('Database') 23 | abstract class DatabaseJsImpl { 24 | external AppJsImpl get app; 25 | external void set app(AppJsImpl a); 26 | external void goOffline(); 27 | external void goOnline(); 28 | external ReferenceJsImpl ref([String path]); 29 | external ReferenceJsImpl refFromURL(String url); 30 | } 31 | 32 | @JS('Reference') 33 | abstract class ReferenceJsImpl extends QueryJsImpl { 34 | external String get key; 35 | external void set key(String s); 36 | external ReferenceJsImpl get parent; 37 | external void set parent(ReferenceJsImpl r); 38 | external ReferenceJsImpl get root; 39 | external void set root(ReferenceJsImpl r); 40 | external ReferenceJsImpl child(String path); 41 | external OnDisconnectJsImpl onDisconnect(); 42 | external ThenableReferenceJsImpl push([value, Func1 onComplete]); 43 | external PromiseJsImpl remove([Func1 onComplete]); 44 | external PromiseJsImpl set(value, [Func1 onComplete]); 45 | external PromiseJsImpl setPriority(priority, [Func1 onComplete]); 46 | external PromiseJsImpl setWithPriority(newVal, newPriority, 47 | [Func1 onComplete]); 48 | external PromiseJsImpl transaction(Func1 transactionUpdate, 49 | [Func3 onComplete, bool applyLocally]); 50 | external PromiseJsImpl update(values, [Func1 onComplete]); 51 | } 52 | 53 | @JS('Query') 54 | abstract class QueryJsImpl { 55 | external ReferenceJsImpl get ref; 56 | external void set ref(ReferenceJsImpl r); 57 | external QueryJsImpl endAt(value, [String key]); 58 | external QueryJsImpl equalTo(value, [String key]); 59 | external bool isEqual(QueryJsImpl other); 60 | external QueryJsImpl limitToFirst(int limit); 61 | external QueryJsImpl limitToLast(int limit); 62 | external void off([String eventType, Func2Opt1 callback, context]); 63 | external Func0 on(String eventType, Func2Opt1 callback, 64 | [cancelCallbackOrContext, context]); 65 | external PromiseJsImpl once(String eventType, 66 | [Func2Opt1 successCallback, failureCallbackOrContext, context]); 67 | external QueryJsImpl orderByChild(String path); 68 | external QueryJsImpl orderByKey(); 69 | external QueryJsImpl orderByPriority(); 70 | external QueryJsImpl orderByValue(); 71 | external QueryJsImpl startAt(value, [String key]); 72 | @override 73 | external String toString(); 74 | } 75 | 76 | @JS('DataSnapshot') 77 | abstract class DataSnapshotJsImpl { 78 | external String get key; 79 | external void set key(String s); 80 | external ReferenceJsImpl get ref; 81 | external void set ref(ReferenceJsImpl r); 82 | external DataSnapshotJsImpl child(String path); 83 | external bool exists(); 84 | external dynamic exportVal(); 85 | external bool forEach(Func1 action); 86 | external dynamic getPriority(); 87 | external bool hasChild(String path); 88 | external bool hasChildren(); 89 | external int numChildren(); 90 | external dynamic val(); 91 | } 92 | 93 | @JS('OnDisconnect') 94 | abstract class OnDisconnectJsImpl { 95 | external PromiseJsImpl cancel([Func1 onComplete]); 96 | external PromiseJsImpl remove([Func1 onComplete]); 97 | external PromiseJsImpl set(value, [Func1 onComplete]); 98 | external PromiseJsImpl setWithPriority(value, priority, [Func1 onComplete]); 99 | external PromiseJsImpl update(values, [Func1 onComplete]); 100 | } 101 | 102 | @JS('ThenableReference') 103 | abstract class ThenableReferenceJsImpl extends ReferenceJsImpl 104 | implements ThenableJsImpl { 105 | @override 106 | external ThenableJsImpl JS$catch([Func1 onReject]); 107 | @override 108 | external ThenableJsImpl then([Func1 onResolve, Func1 onReject]); 109 | } 110 | 111 | @JS() 112 | @anonymous 113 | class TransactionJsImpl { 114 | external bool get committed; 115 | external void set committed(bool c); 116 | external DataSnapshotJsImpl get snapshot; 117 | external void set snapshot(DataSnapshotJsImpl s); 118 | 119 | external factory TransactionJsImpl( 120 | {bool committed, DataSnapshotJsImpl snapshot}); 121 | } 122 | -------------------------------------------------------------------------------- /lib/src/interop/firebase_interop.dart: -------------------------------------------------------------------------------- 1 | @JS('firebase') 2 | library firebase3.firebase_interop; 3 | 4 | import 'package:func/func.dart'; 5 | import 'package:js/js.dart'; 6 | 7 | import 'app_interop.dart'; 8 | import 'auth_interop.dart'; 9 | import 'database_interop.dart'; 10 | import 'storage_interop.dart'; 11 | 12 | external List get apps; 13 | 14 | /// The current SDK version. 15 | /// 16 | /// See: . 17 | external String get SDK_VERSION; 18 | 19 | external AppJsImpl initializeApp(FirebaseOptions options, [String name]); 20 | external AppJsImpl app([String name]); 21 | external AuthJsImpl auth([AppJsImpl app]); 22 | external DatabaseJsImpl database([AppJsImpl app]); 23 | external StorageJsImpl storage([AppJsImpl app]); 24 | 25 | @JS('User') 26 | abstract class UserJsImpl extends UserInfoJsImpl { 27 | external bool get emailVerified; 28 | external void set emailVerified(bool v); 29 | external bool get isAnonymous; 30 | external void set isAnonymous(bool a); 31 | external List get providerData; 32 | external void set providerData(List d); 33 | external String get refreshToken; 34 | external void set refreshToken(String t); 35 | external PromiseJsImpl delete(); 36 | external PromiseJsImpl getToken([bool opt_forceRefresh]); 37 | external PromiseJsImpl link(AuthCredential credential); 38 | external PromiseJsImpl linkWithPopup( 39 | AuthProviderJsImpl provider); 40 | external PromiseJsImpl linkWithRedirect(AuthProviderJsImpl provider); 41 | external PromiseJsImpl reauthenticate(AuthCredential credential); 42 | external PromiseJsImpl reload(); 43 | external PromiseJsImpl sendEmailVerification(); 44 | external PromiseJsImpl unlink(String providerId); 45 | external PromiseJsImpl updateEmail(String newEmail); 46 | external PromiseJsImpl updatePassword(String newPassword); 47 | external PromiseJsImpl updateProfile(UserProfile profile); 48 | } 49 | 50 | @JS('UserInfo') 51 | abstract class UserInfoJsImpl { 52 | external String get displayName; 53 | external void set displayName(String s); 54 | external String get email; 55 | external void set email(String s); 56 | external String get photoURL; 57 | external void set photoURL(String s); 58 | external String get providerId; 59 | external void set providerId(String s); 60 | external String get uid; 61 | external void set uid(String s); 62 | } 63 | 64 | @JS('Promise') 65 | class PromiseJsImpl extends ThenableJsImpl { 66 | external PromiseJsImpl(Function resolver); 67 | external static PromiseJsImpl all(List values); 68 | external static PromiseJsImpl reject(error); 69 | external static PromiseJsImpl resolve(value); 70 | } 71 | 72 | @JS('Thenable') 73 | abstract class ThenableJsImpl { 74 | external ThenableJsImpl JS$catch([Func1 onReject]); 75 | external ThenableJsImpl then([Func1 onResolve, Func1 onReject]); 76 | } 77 | 78 | /// FirebaseError is a subclass of the standard Error object. 79 | /// In addition to a message string, it contains a string-valued code. 80 | /// 81 | /// See: . 82 | @JS() 83 | abstract class FirebaseError { 84 | external String get code; 85 | external void set code(String s); 86 | external String get message; 87 | external void set message(String s); 88 | external String get name; 89 | external void set name(String s); 90 | external String get stack; 91 | external void set stack(String s); 92 | } 93 | 94 | /// A structure for [User]'s user profile. 95 | @JS() 96 | @anonymous 97 | class UserProfile { 98 | external String get displayName; 99 | external void set displayName(String s); 100 | external String get photoURL; 101 | external void set photoURL(String s); 102 | 103 | external factory UserProfile({String displayName, String photoURL}); 104 | } 105 | 106 | /// A structure for options provided to Firebase. 107 | @JS() 108 | @anonymous 109 | class FirebaseOptions { 110 | external String get apiKey; 111 | external void set apiKey(String s); 112 | external String get authDomain; 113 | external void set authDomain(String s); 114 | external String get databaseURL; 115 | external void set databaseURL(String s); 116 | external String get storageBucket; 117 | external void set storageBucket(String s); 118 | 119 | external factory FirebaseOptions( 120 | {String apiKey, 121 | String authDomain, 122 | String databaseURL, 123 | String storageBucket}); 124 | } 125 | -------------------------------------------------------------------------------- /lib/src/interop/js_interop.dart: -------------------------------------------------------------------------------- 1 | @JS() 2 | library firebase3.js_interop; 3 | 4 | import 'package:js/js.dart'; 5 | 6 | @JS("JSON.stringify") 7 | external String stringify(obj); 8 | 9 | @JS("JSON.parse") 10 | external dynamic parse(s); 11 | -------------------------------------------------------------------------------- /lib/src/interop/storage_interop.dart: -------------------------------------------------------------------------------- 1 | @JS('firebase.storage') 2 | library firebase3.storage_interop; 3 | 4 | import 'package:func/func.dart'; 5 | import 'package:js/js.dart'; 6 | 7 | import 'app_interop.dart'; 8 | import 'firebase_interop.dart'; 9 | 10 | @JS('Storage') 11 | abstract class StorageJsImpl { 12 | external AppJsImpl get app; 13 | external void set app(AppJsImpl a); 14 | external int get maxOperationRetryTime; 15 | external void set maxOperationRetryTime(int t); 16 | external int get maxUploadRetryTime; 17 | external void set maxUploadRetryTime(int t); 18 | external ReferenceJsImpl ref([String path]); 19 | external ReferenceJsImpl refFromURL(String url); 20 | external void setMaxOperationRetryTime(int time); 21 | external void setMaxUploadRetryTime(int time); 22 | } 23 | 24 | @JS('Reference') 25 | abstract class ReferenceJsImpl { 26 | external String get bucket; 27 | external void set bucket(String s); 28 | external String get fullPath; 29 | external void set fullPath(String s); 30 | external String get name; 31 | external void set name(String s); 32 | external ReferenceJsImpl get parent; 33 | external void set parent(ReferenceJsImpl r); 34 | external ReferenceJsImpl get root; 35 | external void set root(ReferenceJsImpl r); 36 | external StorageJsImpl get storage; 37 | external void set storage(StorageJsImpl s); 38 | external ReferenceJsImpl child(String path); 39 | external PromiseJsImpl delete(); 40 | external PromiseJsImpl getDownloadURL(); 41 | external PromiseJsImpl getMetadata(); 42 | external UploadTaskJsImpl put(blob, [UploadMetadataJsImpl metadata]); 43 | external UploadTaskJsImpl putString(String value, 44 | [String format, UploadMetadataJsImpl metadata]); 45 | @override 46 | external String toString(); 47 | external PromiseJsImpl updateMetadata( 48 | SettableMetadataJsImpl metadata); 49 | } 50 | 51 | //@JS('FullMetadata') 52 | @JS() 53 | @anonymous 54 | class FullMetadataJsImpl extends UploadMetadataJsImpl { 55 | external String get bucket; 56 | external List get downloadURLs; 57 | external String get fullPath; 58 | external String get generation; 59 | external String get metageneration; 60 | external String get name; 61 | external int get size; 62 | external String get timeCreated; 63 | external String get updated; 64 | 65 | external factory FullMetadataJsImpl( 66 | {String md5Hash, 67 | String cacheControl, 68 | String contentDisposition, 69 | String contentEncoding, 70 | String contentLanguage, 71 | String contentType, 72 | dynamic customMetadata}); 73 | } 74 | 75 | @JS() 76 | @anonymous 77 | class UploadMetadataJsImpl extends SettableMetadataJsImpl { 78 | external String get md5Hash; 79 | external void set md5Hash(String s); 80 | 81 | external factory UploadMetadataJsImpl( 82 | {String md5Hash, 83 | String cacheControl, 84 | String contentDisposition, 85 | String contentEncoding, 86 | String contentLanguage, 87 | String contentType, 88 | dynamic customMetadata}); 89 | } 90 | 91 | @JS('UploadTask') 92 | abstract class UploadTaskJsImpl implements ThenableJsImpl { 93 | external UploadTaskSnapshotJsImpl get snapshot; 94 | external void set snapshot(UploadTaskSnapshotJsImpl t); 95 | external bool cancel(); 96 | external Function on(String event, 97 | [nextOrObserver, Func1 error, Func0 complete]); 98 | external bool pause(); 99 | external bool resume(); 100 | @override 101 | external ThenableJsImpl JS$catch([Func1 onReject]); 102 | @override 103 | external ThenableJsImpl then([Func1 onResolve, Func1 onReject]); 104 | } 105 | 106 | @JS('UploadTaskSnapshot') 107 | abstract class UploadTaskSnapshotJsImpl { 108 | external int get bytesTransferred; 109 | external String get downloadURL; 110 | external FullMetadataJsImpl get metadata; 111 | external ReferenceJsImpl get ref; 112 | external String get state; 113 | external UploadTaskJsImpl get task; 114 | external int get totalBytes; 115 | } 116 | 117 | @JS() 118 | @anonymous 119 | class SettableMetadataJsImpl { 120 | external String get cacheControl; 121 | external void set cacheControl(String s); 122 | external String get contentDisposition; 123 | external void set contentDisposition(String s); 124 | external String get contentEncoding; 125 | external void set contentEncoding(String s); 126 | external String get contentLanguage; 127 | external void set contentLanguage(String s); 128 | external String get contentType; 129 | external void set contentType(String s); 130 | external dynamic get customMetadata; 131 | external void set customMetadata(dynamic s); 132 | external factory SettableMetadataJsImpl( 133 | {String cacheControl, 134 | String contentDisposition, 135 | String contentEncoding, 136 | String contentLanguage, 137 | String contentType, 138 | dynamic customMetadata}); 139 | } 140 | 141 | /// An enumeration of the possible string formats for upload. 142 | @JS() 143 | class StringFormat { 144 | external static String get RAW; 145 | external static String get BASE64; 146 | external static String get BASE64URL; 147 | external static String get DATA_URL; 148 | } 149 | 150 | /// An event that is triggered on a task. 151 | /// 152 | /// See: . 153 | @JS() 154 | abstract class TaskEvent { 155 | external static get STATE_CHANGED; 156 | } 157 | -------------------------------------------------------------------------------- /lib/src/js.dart: -------------------------------------------------------------------------------- 1 | /// This class is a wrapper for the jsObject. All the specific JsObject 2 | /// wrappers extend from it. 3 | abstract class JsObjectWrapper { 4 | /// JS object. 5 | T jsObject; 6 | 7 | /// Creates a new JsObjectWrapper type from a [jsObject]. 8 | JsObjectWrapper.fromJsObject(this.jsObject); 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:js/js.dart'; 4 | 5 | import 'app.dart'; 6 | import 'interop/storage_interop.dart' as storage_interop; 7 | import 'js.dart'; 8 | import 'utils.dart'; 9 | 10 | export 'interop/storage_interop.dart' show StringFormat; 11 | 12 | /// Represents the current state of a running upload. 13 | /// 14 | /// See: . 15 | enum TaskState { RUNNING, PAUSED, SUCCESS, CANCELED, ERROR } 16 | 17 | /// A service for uploading and downloading large objects to and from the 18 | /// Google Cloud Storage. 19 | /// 20 | /// See: 21 | class Storage extends JsObjectWrapper { 22 | App _app; 23 | 24 | /// App for this instance of storage service. 25 | App get app { 26 | if (_app != null) { 27 | _app.jsObject = jsObject.app; 28 | } else { 29 | _app = new App.fromJsObject(jsObject.app); 30 | } 31 | return _app; 32 | } 33 | 34 | /// Returns the maximum time to retry operations other than uploads 35 | /// or downloads (in milliseconds). 36 | int get maxOperationRetryTime => jsObject.maxOperationRetryTime; 37 | 38 | /// Returns the maximum time to retry uploads (in milliseconds). 39 | int get maxUploadRetryTime => jsObject.maxUploadRetryTime; 40 | 41 | /// Creates a new Storage from a [jsObject]. 42 | Storage.fromJsObject(storage_interop.StorageJsImpl jsObject) 43 | : super.fromJsObject(jsObject); 44 | 45 | /// Returns a [StorageReference] for the given [path] in the default bucket. 46 | StorageReference ref([String path]) => 47 | new StorageReference.fromJsObject(jsObject.ref(path)); 48 | 49 | /// Returns a [StorageReference] for the given absolute [url]. 50 | StorageReference refFromURL(String url) => 51 | new StorageReference.fromJsObject(jsObject.refFromURL(url)); 52 | 53 | /// Sets the maximum operation retry time to a value of [time]. 54 | void setMaxOperationRetryTime(int time) => 55 | jsObject.setMaxOperationRetryTime(time); 56 | 57 | /// Sets the maximum upload retry time to a value of [time]. 58 | void setMaxUploadRetryTime(int time) => jsObject.setMaxUploadRetryTime(time); 59 | } 60 | 61 | /// StorageReference is a reference to a Google Cloud Storage object. 62 | /// It is possible to upload, download, and delete objects, as well as 63 | /// get or set the object metadata. 64 | /// 65 | /// See: 66 | class StorageReference 67 | extends JsObjectWrapper { 68 | /// The name of the bucket. 69 | String get bucket => jsObject.bucket; 70 | 71 | /// The full path. 72 | String get fullPath => jsObject.fullPath; 73 | 74 | /// The short name. Which is the last component of the full path. 75 | String get name => jsObject.name; 76 | 77 | StorageReference _parent; 78 | 79 | /// The reference to the parent location of this reference. 80 | /// It is [null] in case of root StorageReference. 81 | StorageReference get parent { 82 | if (jsObject.parent != null) { 83 | if (_parent != null) { 84 | _parent.jsObject = jsObject.parent; 85 | } else { 86 | _parent = new StorageReference.fromJsObject(jsObject.parent); 87 | } 88 | } else { 89 | _parent = null; 90 | } 91 | return _parent; 92 | } 93 | 94 | StorageReference _root; 95 | 96 | /// The reference to the root of this storage reference's bucket. 97 | StorageReference get root { 98 | if (_root != null) { 99 | _root.jsObject = jsObject.root; 100 | } else { 101 | _root = new StorageReference.fromJsObject(jsObject.root); 102 | } 103 | return _root; 104 | } 105 | 106 | Storage _storage; 107 | 108 | /// The [Storage] service associated with this reference. 109 | Storage get storage { 110 | if (_storage != null) { 111 | _storage.jsObject = jsObject.storage; 112 | } else { 113 | _storage = new Storage.fromJsObject(jsObject.storage); 114 | } 115 | return _storage; 116 | } 117 | 118 | /// Creates a new StorageReference from a [jsObject]. 119 | StorageReference.fromJsObject(storage_interop.ReferenceJsImpl jsObject) 120 | : super.fromJsObject(jsObject); 121 | 122 | /// Returns a child StorageReference to a relative [path] 123 | /// from the actual reference. 124 | StorageReference child(String path) => 125 | new StorageReference.fromJsObject(jsObject.child(path)); 126 | 127 | /// Deletes the object at the actual location. 128 | Future delete() => handleThenable(jsObject.delete()); 129 | 130 | /// Returns a long lived download URL for this reference. 131 | Future getDownloadURL() async { 132 | var uriString = await handleThenable(jsObject.getDownloadURL()); 133 | return Uri.parse(uriString); 134 | } 135 | 136 | /// Returns a [FullMetadata] from this reference at actual location. 137 | Future getMetadata() => handleThenableWithMapper( 138 | jsObject.getMetadata(), (m) => new FullMetadata.fromJsObject(m)); 139 | 140 | /// Uploads data [blob] to the actual location with optional [metadata]. 141 | /// Returns the [UploadTask] which can be used to monitor and manage 142 | /// the upload. 143 | UploadTask put(blob, [UploadMetadata metadata]) { 144 | storage_interop.UploadTaskJsImpl taskImpl; 145 | if (metadata != null) { 146 | taskImpl = jsObject.put(blob, metadata.jsObject); 147 | } else { 148 | taskImpl = jsObject.put(blob); 149 | } 150 | return new UploadTask.fromJsObject(taskImpl); 151 | } 152 | 153 | /// Uploads String [data] to the actual location with optional String [format] 154 | /// and [metadata]. 155 | /// Returns the [UploadTask] which can be used to monitor and manage 156 | /// the upload. 157 | UploadTask putString(String data, [String format, UploadMetadata metadata]) { 158 | storage_interop.UploadTaskJsImpl taskImpl; 159 | if (metadata != null) { 160 | taskImpl = jsObject.putString(data, format, metadata.jsObject); 161 | } else if (format != null) { 162 | taskImpl = jsObject.putString(data, format); 163 | } else { 164 | taskImpl = jsObject.putString(data); 165 | } 166 | return new UploadTask.fromJsObject(taskImpl); 167 | } 168 | 169 | /// Returns the String representation of the current storage reference. 170 | @override 171 | String toString() => jsObject.toString(); 172 | 173 | /// Updates metadata from this reference at actual location with 174 | /// the new [metadata]. 175 | Future updateMetadata(SettableMetadata metadata) => 176 | handleThenableWithMapper(jsObject.updateMetadata(metadata.jsObject), 177 | (m) => new FullMetadata.fromJsObject(m)); 178 | } 179 | 180 | /// The full set of object metadata, including read-only properties. 181 | /// 182 | /// See: 183 | class FullMetadata 184 | extends _UploadMetadataBase { 185 | /// The bucket the actual object is contained in. 186 | String get bucket => jsObject.bucket; 187 | 188 | /// Returns an array of long-lived download URLs. With at least one URL. 189 | List get downloadURLs => jsObject.downloadURLs.map(Uri.parse).toList(); 190 | 191 | /// The full path. 192 | String get fullPath => jsObject.fullPath; 193 | 194 | /// The generation. 195 | String get generation => jsObject.generation; 196 | 197 | /// The metageneration. 198 | String get metageneration => jsObject.metageneration; 199 | 200 | /// The short name. Which is the last component of the full path. 201 | String get name => jsObject.name; 202 | 203 | /// The size in bytes. 204 | int get size => jsObject.size; 205 | 206 | /// Returns the time it was created as a [DateTime]. 207 | DateTime get timeCreated => DateTime.parse(jsObject.timeCreated); 208 | 209 | /// Returns the time it was last updated as a [DateTime]. 210 | DateTime get updated => DateTime.parse(jsObject.updated); 211 | 212 | /// Creates a new FullMetadata with optional metadata parameters. 213 | factory FullMetadata( 214 | {String bucket, 215 | List downloadURLs, 216 | String fullPath, 217 | String generation, 218 | String metageneration, 219 | String name, 220 | int size, 221 | String timeCreated, 222 | String updated, 223 | String md5Hash, 224 | String cacheControl, 225 | String contentDisposition, 226 | String contentEncoding, 227 | String contentLanguage, 228 | String contentType, 229 | Map customMetadata}) => 230 | new FullMetadata.fromJsObject(new storage_interop.FullMetadataJsImpl( 231 | md5Hash: md5Hash, 232 | cacheControl: cacheControl, 233 | contentDisposition: contentDisposition, 234 | contentEncoding: contentEncoding, 235 | contentLanguage: contentLanguage, 236 | contentType: contentType, 237 | customMetadata: 238 | (customMetadata != null) ? jsify(customMetadata) : null)); 239 | 240 | /// Creates a new FullMetadata from a [jsObject]. 241 | FullMetadata.fromJsObject(jsObject) : super.fromJsObject(jsObject); 242 | } 243 | 244 | /// Object metadata that can be set at upload. 245 | /// 246 | /// See: . 247 | class UploadMetadata 248 | extends _UploadMetadataBase { 249 | /// Creates a new UploadMetadata with optional metadata parameters. 250 | factory UploadMetadata( 251 | {String md5Hash, 252 | String cacheControl, 253 | String contentDisposition, 254 | String contentEncoding, 255 | String contentLanguage, 256 | String contentType, 257 | Map customMetadata}) => 258 | new UploadMetadata.fromJsObject(new storage_interop.UploadMetadataJsImpl( 259 | md5Hash: md5Hash, 260 | cacheControl: cacheControl, 261 | contentDisposition: contentDisposition, 262 | contentEncoding: contentEncoding, 263 | contentLanguage: contentLanguage, 264 | contentType: contentType, 265 | customMetadata: 266 | (customMetadata != null) ? jsify(customMetadata) : null)); 267 | 268 | /// Creates a new UploadMetadata from a [jsObject]. 269 | UploadMetadata.fromJsObject(storage_interop.UploadMetadataJsImpl jsObject) 270 | : super.fromJsObject(jsObject); 271 | } 272 | 273 | abstract class _UploadMetadataBase< 274 | T extends storage_interop.UploadMetadataJsImpl> 275 | extends _SettableMetadataBase { 276 | /// The Base64-encoded MD5 hash for the object being uploaded. 277 | String get md5Hash => jsObject.md5Hash; 278 | void set md5Hash(String s) { 279 | jsObject.md5Hash = s; 280 | } 281 | 282 | _UploadMetadataBase.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 283 | } 284 | 285 | /// Represents the process of uploading an object, and allows to monitor 286 | /// and manage the upload. 287 | /// 288 | /// See: . 289 | class UploadTask extends JsObjectWrapper { 290 | Future _future; 291 | 292 | /// Returns the UploadTaskSnapshot when the upload successfully completes. 293 | Future get future { 294 | if (_future == null) { 295 | _future = handleThenableWithMapper( 296 | jsObject, (val) => new UploadTaskSnapshot.fromJsObject(val)); 297 | } 298 | return _future; 299 | } 300 | 301 | UploadTaskSnapshot _snapshot; 302 | 303 | /// Returns the upload task snapshot of the current task state. 304 | UploadTaskSnapshot get snapshot { 305 | if (_snapshot != null) { 306 | _snapshot.jsObject = jsObject.snapshot; 307 | } else { 308 | _snapshot = new UploadTaskSnapshot.fromJsObject(jsObject.snapshot); 309 | } 310 | return _snapshot; 311 | } 312 | 313 | /// Creates a new UploadTask from a [jsObject]. 314 | UploadTask.fromJsObject(storage_interop.UploadTaskJsImpl jsObject) 315 | : super.fromJsObject(jsObject); 316 | 317 | /// Cancels a running task. Has no effect on a complete or failed task. 318 | /// Returns [:true:] if it had an effect. 319 | bool cancel() => jsObject.cancel(); 320 | 321 | var _onStateChangedUnsubscribe; 322 | StreamController _changeController; 323 | 324 | /// Stream for upload task state changed event. 325 | Stream get onStateChanged { 326 | if (_changeController == null) { 327 | var nextWrapper = 328 | allowInterop((storage_interop.UploadTaskSnapshotJsImpl data) { 329 | _changeController.add(new UploadTaskSnapshot.fromJsObject(data)); 330 | }); 331 | 332 | var errorWrapper = allowInterop((e) => _changeController.addError(e)); 333 | var onCompletion = allowInterop(() => _changeController.close()); 334 | 335 | void startListen() { 336 | _onStateChangedUnsubscribe = jsObject.on( 337 | storage_interop.TaskEvent.STATE_CHANGED, 338 | nextWrapper, 339 | errorWrapper, 340 | onCompletion); 341 | } 342 | 343 | void stopListen() { 344 | _onStateChangedUnsubscribe(); 345 | } 346 | 347 | _changeController = new StreamController.broadcast( 348 | onListen: startListen, onCancel: stopListen, sync: true); 349 | } 350 | return _changeController.stream; 351 | } 352 | 353 | /// Pauses the running task. Has no effect on a paused or failed task. 354 | /// Returns [:true:] if it had an effect. 355 | bool pause() => jsObject.pause(); 356 | 357 | /// Resumes the paused task. Has no effect on a running or failed task. 358 | /// Returns [:true:] if it had an effect. 359 | bool resume() => jsObject.resume(); 360 | } 361 | 362 | /// Holds data about the current state of the upload task. 363 | /// 364 | /// See: . 365 | class UploadTaskSnapshot 366 | extends JsObjectWrapper { 367 | /// The number of bytes that have been successfully transferred. 368 | int get bytesTransferred => jsObject.bytesTransferred; 369 | 370 | /// Contains a long-lived download URL for the object after the upload 371 | /// completes. It is also accessible from [metadata]. 372 | Uri get downloadURL => Uri.parse(jsObject.downloadURL); 373 | 374 | FullMetadata _metadata; 375 | 376 | /// The metadata. Before the upload completes, it contains the metadata sent 377 | /// to the server. After the upload completes, it contains the metadata sent 378 | /// back from the server. 379 | FullMetadata get metadata { 380 | if (jsObject.metadata != null) { 381 | if (_metadata != null) { 382 | _metadata.jsObject = jsObject.metadata; 383 | } else { 384 | _metadata = new FullMetadata.fromJsObject(jsObject.metadata); 385 | } 386 | } else { 387 | _metadata = null; 388 | } 389 | return _metadata; 390 | } 391 | 392 | StorageReference _ref; 393 | 394 | /// The StorageReference that spawned the current snapshot's upload task. 395 | StorageReference get ref { 396 | if (_ref != null) { 397 | _ref.jsObject = jsObject.ref; 398 | } else { 399 | _ref = new StorageReference.fromJsObject(jsObject.ref); 400 | } 401 | return _ref; 402 | } 403 | 404 | /// The actual task state. 405 | TaskState get state { 406 | switch (jsObject.state) { 407 | case "running": 408 | return TaskState.RUNNING; 409 | case "paused": 410 | return TaskState.PAUSED; 411 | case "success": 412 | return TaskState.SUCCESS; 413 | case "canceled": 414 | return TaskState.CANCELED; 415 | case "error": 416 | return TaskState.ERROR; 417 | default: 418 | throw new UnsupportedError( 419 | 'Unknown state "${jsObject.state}" please file a bug.'); 420 | } 421 | } 422 | 423 | UploadTask _task; 424 | 425 | /// The UploadTask for this snapshot. 426 | UploadTask get task { 427 | if (_task != null) { 428 | _task.jsObject = jsObject.task; 429 | } else { 430 | _task = new UploadTask.fromJsObject(jsObject.task); 431 | } 432 | return _task; 433 | } 434 | 435 | /// The total number of bytes to be uploaded. 436 | int get totalBytes => jsObject.totalBytes; 437 | 438 | /// Creates a new UploadTaskSnapshot from a [jsObject]. 439 | UploadTaskSnapshot.fromJsObject( 440 | storage_interop.UploadTaskSnapshotJsImpl jsObject) 441 | : super.fromJsObject(jsObject); 442 | } 443 | 444 | /// Object metadata that can be set at any time. 445 | /// 446 | /// See: . 447 | class SettableMetadata 448 | extends _SettableMetadataBase { 449 | /// Creates a new SettableMetadata with optional metadata parameters. 450 | factory SettableMetadata( 451 | {String cacheControl, 452 | String contentDisposition, 453 | String contentEncoding, 454 | String contentLanguage, 455 | String contentType, 456 | Map customMetadata}) => 457 | new SettableMetadata.fromJsObject( 458 | new storage_interop.SettableMetadataJsImpl( 459 | cacheControl: cacheControl, 460 | contentDisposition: contentDisposition, 461 | contentEncoding: contentEncoding, 462 | contentLanguage: contentLanguage, 463 | contentType: contentType, 464 | customMetadata: 465 | (customMetadata != null) ? jsify(customMetadata) : null)); 466 | 467 | /// Creates a new SettableMetadata from a [jsObject]. 468 | SettableMetadata.fromJsObject(storage_interop.SettableMetadataJsImpl jsObject) 469 | : super.fromJsObject(jsObject); 470 | } 471 | 472 | abstract class _SettableMetadataBase< 473 | T extends storage_interop.SettableMetadataJsImpl> 474 | extends JsObjectWrapper { 475 | /// Served as the 'Cache-Control' header on object download. 476 | String get cacheControl => jsObject.cacheControl; 477 | void set cacheControl(String s) { 478 | jsObject.cacheControl = s; 479 | } 480 | 481 | /// Served as the 'Content-Disposition' header on object download. 482 | String get contentDisposition => jsObject.contentDisposition; 483 | void set contentDisposition(String s) { 484 | jsObject.contentDisposition = s; 485 | } 486 | 487 | /// Served as the 'Content-Encoding' header on object download. 488 | String get contentEncoding => jsObject.contentEncoding; 489 | void set contentEncoding(String s) { 490 | jsObject.contentEncoding = s; 491 | } 492 | 493 | /// Served as the 'Content-Language' header on object download. 494 | String get contentLanguage => jsObject.contentLanguage; 495 | void set contentLanguage(String s) { 496 | jsObject.contentLanguage = s; 497 | } 498 | 499 | /// Served as the 'Content-Type' header on object download. 500 | String get contentType => jsObject.contentType; 501 | void set contentType(String s) { 502 | jsObject.contentType = s; 503 | } 504 | 505 | /// Additional user-defined custom metadata. 506 | Map get customMetadata => 507 | dartify(jsObject.customMetadata) as Map; 508 | void set customMetadata(Map m) { 509 | jsObject.customMetadata = jsify(m); 510 | } 511 | 512 | _SettableMetadataBase.fromJsObject(T jsObject) : super.fromJsObject(jsObject); 513 | } 514 | -------------------------------------------------------------------------------- /lib/src/top_level.dart: -------------------------------------------------------------------------------- 1 | import 'app.dart'; 2 | import 'auth.dart'; 3 | import 'database.dart'; 4 | import 'interop/firebase_interop.dart' as firebase; 5 | import 'storage.dart'; 6 | 7 | export 'interop/firebase_interop.dart' show SDK_VERSION; 8 | 9 | /// A (read-only) array of all the initialized Apps. 10 | /// 11 | /// See: . 12 | List get apps => 13 | firebase.apps.map((jsApp) => new App.fromJsObject(jsApp)).toList(); 14 | 15 | const String _defaultAppName = "[DEFAULT]"; 16 | 17 | /// Creates (and initializes) a Firebase App with API key, auth domain, 18 | /// database URL and storage bucket. 19 | /// 20 | /// See: . 21 | App initializeApp( 22 | {String apiKey, 23 | String authDomain, 24 | String databaseURL, 25 | String storageBucket, 26 | String name}) { 27 | name ??= _defaultAppName; 28 | 29 | return new App.fromJsObject(firebase.initializeApp( 30 | new firebase.FirebaseOptions( 31 | apiKey: apiKey, 32 | authDomain: authDomain, 33 | databaseURL: databaseURL, 34 | storageBucket: storageBucket), 35 | name)); 36 | } 37 | 38 | App _app; 39 | 40 | /// Retrieves an instance of an [App]. 41 | /// 42 | /// With no arguments, this returns the default App. With a single 43 | /// string argument, it returns the named App. 44 | /// 45 | /// This function throws an exception if the app you are trying 46 | /// to access does not exist. 47 | /// 48 | /// See: . 49 | App app([String name]) { 50 | var jsObject = (name != null) ? firebase.app(name) : firebase.app(); 51 | 52 | if (_app != null) { 53 | _app.jsObject = jsObject; 54 | } else { 55 | _app = new App.fromJsObject(jsObject); 56 | } 57 | return _app; 58 | } 59 | 60 | Auth _auth; 61 | 62 | /// Gets the [Auth] object for the default App or a given App. 63 | /// 64 | /// See: . 65 | Auth auth([App app]) { 66 | var jsObject = (app != null) ? firebase.auth(app.jsObject) : firebase.auth(); 67 | 68 | if (_auth != null) { 69 | _auth.jsObject = jsObject; 70 | } else { 71 | _auth = new Auth.fromJsObject(jsObject); 72 | } 73 | return _auth; 74 | } 75 | 76 | Database _database; 77 | 78 | /// Accesses the [Database] service for the default App or a given app. 79 | /// 80 | /// The database is also a namespace that can be used to access 81 | /// global constants and methods associated with the database service. 82 | /// 83 | /// See: . 84 | Database database([App app]) { 85 | var jsObject = 86 | (app != null) ? firebase.database(app.jsObject) : firebase.database(); 87 | 88 | if (_database != null) { 89 | _database.jsObject = jsObject; 90 | } else { 91 | _database = new Database.fromJsObject(jsObject); 92 | } 93 | return _database; 94 | } 95 | 96 | Storage _storage; 97 | 98 | /// The namespace for all the [Storage] functionality. 99 | /// 100 | /// The returned service is initialized with a particular app which contains 101 | /// the project's storage location, or uses the default app if none is provided. 102 | /// 103 | /// See: . 104 | Storage storage([App app]) { 105 | var jsObject = 106 | (app != null) ? firebase.storage(app.jsObject) : firebase.storage(); 107 | 108 | if (_storage != null) { 109 | _storage.jsObject = jsObject; 110 | } else { 111 | _storage = new Storage.fromJsObject(jsObject); 112 | } 113 | return _storage; 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:js'; 4 | 5 | import 'package:func/func.dart'; 6 | import 'package:js/js.dart'; 7 | 8 | import 'interop/firebase_interop.dart'; 9 | import 'interop/js_interop.dart' as js; 10 | 11 | /// Returns Dart representation from JS Object. 12 | dynamic dartify(Object jsObject) { 13 | if (_isBasicType(jsObject)) { 14 | return jsObject; 15 | } 16 | 17 | var json = js.stringify(jsObject); 18 | return JSON.decode(json); 19 | } 20 | 21 | /// Returns the JS implementation from Dart Object. 22 | dynamic jsify(Object dartObject) { 23 | if (_isBasicType(dartObject)) { 24 | return dartObject; 25 | } 26 | 27 | Object json; 28 | try { 29 | json = JSON.encode(dartObject, toEncodable: _noCustomEncodable); 30 | } on JsonUnsupportedObjectError { 31 | throw new ArgumentError("Only basic JS types are supported"); 32 | } 33 | return js.parse(json); 34 | } 35 | 36 | /// Returns [:true:] if the [value] is a very basic built-in type - e.g. 37 | /// [null], [num], [bool] or [String]. It returns [:false:] in the other case. 38 | bool _isBasicType(value) { 39 | if (value == null || value is num || value is bool || value is String) { 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | _noCustomEncodable(value) => 46 | throw new UnsupportedError("Object with toJson shouldn't work either"); 47 | 48 | /// Handles the [thenable] object. 49 | Future/**/ handleThenable/**/(ThenableJsImpl thenable) { 50 | var completer = new Completer/**/(); 51 | 52 | thenable.then(allowInterop(([value]) { 53 | completer.complete(value); 54 | }), resolveError(completer)); 55 | return completer.future; 56 | } 57 | 58 | /// Handles the [thenable] object with provided [mapper] function. 59 | Future/**/ handleThenableWithMapper/**/( 60 | ThenableJsImpl thenable, Func1/**/ mapper) { 61 | var completer = new Completer/**/(); 62 | 63 | thenable.then(allowInterop((val) { 64 | var mappedValue = mapper(val); 65 | completer.complete(mappedValue); 66 | }), resolveError(completer)); 67 | return completer.future; 68 | } 69 | 70 | /// Resolves error. 71 | VoidFunc1 resolveError(Completer c) => allowInterop(c.completeError); 72 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: firebase3 2 | description: DEPRECATED Dart wrapper library for the new Firebase. 3 | version: 0.2.3-dev 4 | author: Jana Moudra 5 | homepage: https://github.com/Janamou/firebase3-dart 6 | 7 | environment: 8 | sdk: '>=1.18.0 <2.0.0' 9 | 10 | dependencies: 11 | js: ">=0.6.0 <0.7.0" 12 | func: ">=0.1.0 <0.2.0" 13 | browser: '>=0.10.0 <0.11.0' 14 | 15 | dev_dependencies: 16 | googleapis_auth: '^0.2.3+5' 17 | http: '^0.11.3' 18 | path: '^1.3.9' 19 | test: '>=0.12.17 <0.13.0' 20 | -------------------------------------------------------------------------------- /test/app_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('browser') 2 | import 'package:firebase3/firebase.dart' as fb; 3 | import 'package:firebase3/src/assets/assets.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | void main() { 7 | group("App", () { 8 | setUpAll(() async { 9 | await config(); 10 | }); 11 | 12 | group('instance', () { 13 | fb.App app; 14 | 15 | setUpAll(() { 16 | app = fb.initializeApp( 17 | apiKey: apiKey, 18 | authDomain: authDomain, 19 | databaseURL: databaseUrl, 20 | storageBucket: storageBucket); 21 | }); 22 | 23 | test("Exists", () { 24 | expect(app, isNotNull); 25 | expect(fb.app(), isNotNull); 26 | expect(fb.apps.first.name, app.name); 27 | }); 28 | 29 | test("Is [DEFAULT]", () { 30 | expect(app.name, "[DEFAULT]"); 31 | }); 32 | 33 | test("Has options", () { 34 | expect(app.options, isNotNull); 35 | expect(app.options.apiKey, apiKey); 36 | expect(app.options.storageBucket, storageBucket); 37 | expect(app.options.authDomain, authDomain); 38 | expect(app.options.databaseURL, databaseUrl); 39 | }); 40 | 41 | test("Get database", () { 42 | expect(app.database(), isNotNull); 43 | }); 44 | 45 | test("Get Auth", () { 46 | expect(app.auth(), isNotNull); 47 | }); 48 | 49 | test("Get storage", () { 50 | expect(app.storage(), isNotNull); 51 | }); 52 | }); 53 | 54 | test("Can be created with name", () { 55 | var app2 = fb.initializeApp( 56 | apiKey: apiKey, 57 | authDomain: authDomain, 58 | databaseURL: databaseUrl, 59 | storageBucket: storageBucket, 60 | name: "MySuperApp"); 61 | 62 | expect(app2, isNotNull); 63 | expect(fb.app("MySuperApp"), isNotNull); 64 | expect(app2.name, "MySuperApp"); 65 | expect(fb.apps.length, 2); //[DEFAULT] and MySuperApp 66 | }); 67 | 68 | test("Can be deleted", () async { 69 | fb.initializeApp( 70 | apiKey: apiKey, 71 | authDomain: authDomain, 72 | databaseURL: databaseUrl, 73 | storageBucket: storageBucket, 74 | name: "MyDeletedApp"); 75 | 76 | expect(fb.app("MyDeletedApp"), isNotNull); 77 | expect(fb.apps.where((app) => app.name == "MyDeletedApp").toList(), 78 | isNotEmpty); 79 | 80 | await fb.app("MyDeletedApp").delete(); 81 | expect( 82 | fb.apps.where((app) => app.name == "MyDeletedApp").toList(), isEmpty); 83 | }); 84 | }); 85 | 86 | group("Firebase", () { 87 | test("SDK version", () { 88 | expect(fb.SDK_VERSION, startsWith("3.")); 89 | }); 90 | 91 | group('ServerValue', () { 92 | test('TIMESTAMP', () { 93 | expect(fb.ServerValue.TIMESTAMP, isNotNull); 94 | }); 95 | }); 96 | }); 97 | } 98 | -------------------------------------------------------------------------------- /test/app_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | App Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/auth_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('browser') 2 | import 'package:firebase3/firebase.dart'; 3 | import 'package:firebase3/src/assets/assets.dart'; 4 | import 'package:test/test.dart'; 5 | import 'test_util.dart'; 6 | 7 | void main() { 8 | App app; 9 | 10 | setUpAll(() async { 11 | await config(); 12 | }); 13 | 14 | setUp(() async { 15 | app = initializeApp( 16 | apiKey: apiKey, 17 | authDomain: authDomain, 18 | databaseURL: databaseUrl, 19 | storageBucket: storageBucket); 20 | }); 21 | 22 | tearDown(() async { 23 | if (app != null) { 24 | await app.delete(); 25 | app = null; 26 | } 27 | }); 28 | 29 | group('providers', () { 30 | group('Email', () { 31 | test('PROVIDER_ID', () { 32 | expect(EmailAuthProvider.PROVIDER_ID, 'password'); 33 | }); 34 | test('instance', () { 35 | var provider = new EmailAuthProvider(); 36 | expect(provider.providerId, EmailAuthProvider.PROVIDER_ID); 37 | }); 38 | test('credential', () { 39 | var cred = EmailAuthProvider.credential('un', 'pw'); 40 | expect(cred.provider, equals(EmailAuthProvider.PROVIDER_ID)); 41 | }); 42 | }); 43 | 44 | group('Facebook', () { 45 | test('PROVIDER_ID', () { 46 | expect(FacebookAuthProvider.PROVIDER_ID, 'facebook.com'); 47 | }); 48 | test('instance', () { 49 | var provider = new FacebookAuthProvider(); 50 | expect(provider.providerId, FacebookAuthProvider.PROVIDER_ID); 51 | }); 52 | test('credential', () { 53 | var cred = FacebookAuthProvider.credential('token'); 54 | expect(cred.provider, equals(FacebookAuthProvider.PROVIDER_ID)); 55 | }); 56 | }); 57 | 58 | group('GitHub', () { 59 | test('PROVIDER_ID', () { 60 | expect(GithubAuthProvider.PROVIDER_ID, 'github.com'); 61 | }); 62 | test('instance', () { 63 | var provider = new GithubAuthProvider(); 64 | expect(provider.providerId, GithubAuthProvider.PROVIDER_ID); 65 | }); 66 | test('credential', () { 67 | var cred = GithubAuthProvider.credential('token'); 68 | expect(cred.provider, equals(GithubAuthProvider.PROVIDER_ID)); 69 | }); 70 | }); 71 | 72 | group('Google', () { 73 | test('PROVIDER_ID', () { 74 | expect(GoogleAuthProvider.PROVIDER_ID, 'google.com'); 75 | }); 76 | test('instance', () { 77 | var provider = new GoogleAuthProvider(); 78 | expect(provider.providerId, GoogleAuthProvider.PROVIDER_ID); 79 | }); 80 | test('credential', () { 81 | var cred = GoogleAuthProvider.credential('idToken', 'accessToken'); 82 | expect(cred.provider, equals(GoogleAuthProvider.PROVIDER_ID)); 83 | }); 84 | }); 85 | 86 | group('Twitter', () { 87 | test('PROVIDER_ID', () { 88 | expect(TwitterAuthProvider.PROVIDER_ID, 'twitter.com'); 89 | }); 90 | test('instance', () { 91 | var provider = new TwitterAuthProvider(); 92 | expect(provider.providerId, TwitterAuthProvider.PROVIDER_ID); 93 | }); 94 | test('credential', () { 95 | var cred = TwitterAuthProvider.credential('token', 'secret'); 96 | expect(cred.provider, equals(TwitterAuthProvider.PROVIDER_ID)); 97 | }); 98 | }); 99 | }); 100 | 101 | group('anonymous user', () { 102 | Auth authValue; 103 | User user; 104 | setUp(() async { 105 | authValue = auth(); 106 | expect(authValue.currentUser, isNull); 107 | 108 | try { 109 | user = await authValue.signInAnonymously(); 110 | } on FirebaseError catch (e) { 111 | printException(e); 112 | rethrow; 113 | } 114 | }); 115 | 116 | tearDown(() async { 117 | if (user != null) { 118 | await user.delete(); 119 | user = null; 120 | } 121 | }); 122 | 123 | test('properties', () { 124 | expect(user.isAnonymous, isTrue); 125 | expect(user.emailVerified, isFalse); 126 | expect(user.providerData, isEmpty); 127 | expect(user.providerId, 'firebase'); 128 | }); 129 | 130 | test('delete', () async { 131 | await user.delete(); 132 | expect(authValue.currentUser, isNull); 133 | 134 | try { 135 | await user.delete(); 136 | fail('user.delete should throw'); 137 | } on FirebaseError catch (e) { 138 | expect(e.code, 'auth/app-deleted'); 139 | } catch (e) { 140 | fail('Should have been a FirebaseError'); 141 | } 142 | user = null; 143 | }); 144 | }); 145 | 146 | group('user', () { 147 | Auth authValue; 148 | User user; 149 | 150 | setUp(() { 151 | authValue = auth(); 152 | expect(authValue.currentUser, isNull); 153 | }); 154 | 155 | tearDown(() async { 156 | if (user != null) { 157 | await user.delete(); 158 | user = null; 159 | } 160 | }); 161 | 162 | test('create user with email and password', () async { 163 | try { 164 | user = await authValue.createUserWithEmailAndPassword( 165 | "some_user@example.com", "janicka"); 166 | expect(user, isNotNull); 167 | expect(user.email, "some_user@example.com"); 168 | } on FirebaseError catch (e) { 169 | printException(e); 170 | rethrow; 171 | } 172 | }); 173 | }); 174 | 175 | group('registered user', () { 176 | Auth authValue; 177 | User user; 178 | 179 | setUp(() async { 180 | authValue = auth(); 181 | 182 | try { 183 | user = await authValue.createUserWithEmailAndPassword( 184 | "other_user@example.com", "hesloheslo"); 185 | expect(authValue.currentUser, isNotNull); 186 | } on FirebaseError catch (e) { 187 | printException(e); 188 | rethrow; 189 | } 190 | }); 191 | 192 | tearDown(() async { 193 | if (user != null) { 194 | await user.delete(); 195 | user = null; 196 | } 197 | }); 198 | 199 | test('update profile', () async { 200 | try { 201 | expect(user, isNotNull); 202 | expect(user.displayName, isNull); 203 | 204 | var profile = new UserProfile(displayName: "Other User"); 205 | await user.updateProfile(profile); 206 | expect(user.displayName, "Other User"); 207 | } on FirebaseError catch (e) { 208 | printException(e); 209 | rethrow; 210 | } 211 | }); 212 | }); 213 | } 214 | -------------------------------------------------------------------------------- /test/auth_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Authentication Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/database_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('browser') 2 | import 'package:firebase3/firebase.dart' as fb; 3 | import 'package:firebase3/src/assets/assets.dart'; 4 | import 'package:test/test.dart'; 5 | import 'package:firebase3/firebase.dart'; 6 | 7 | import 'test_util.dart'; 8 | 9 | void main() { 10 | App app; 11 | 12 | setUpAll(() async { 13 | await config(); 14 | }); 15 | 16 | setUp(() async { 17 | app = initializeApp( 18 | apiKey: apiKey, 19 | authDomain: authDomain, 20 | databaseURL: databaseUrl, 21 | storageBucket: storageBucket); 22 | }); 23 | 24 | tearDown(() async { 25 | if (app != null) { 26 | await app.delete(); 27 | app = null; 28 | } 29 | }); 30 | 31 | group("Database", () { 32 | Database database; 33 | 34 | setUp(() { 35 | database = fb.database(); 36 | }); 37 | 38 | group("instance", () { 39 | test("App exists", () { 40 | expect(database, isNotNull); 41 | expect(database.app, isNotNull); 42 | expect(database.app.name, fb.app().name); 43 | }); 44 | }); 45 | 46 | group("DatabaseReference", () { 47 | DatabaseReference ref; 48 | String key; 49 | 50 | setUp(() { 51 | ref = database.ref(validDatePath()); 52 | key = ref.push({"text": "hello"}).key; 53 | expect(key, isNotNull); 54 | }); 55 | 56 | tearDown(() async { 57 | await ref.remove(); 58 | ref = null; 59 | key = null; 60 | }); 61 | 62 | test("remove", () async { 63 | var eventFuture = ref.onValue.first; 64 | 65 | await ref.remove(); 66 | var event = await eventFuture; 67 | 68 | expect(event.snapshot.val(), isNull); 69 | }); 70 | 71 | test("child and once on value", () async { 72 | var childRef = ref.child(key); 73 | var event = await childRef.once("value"); 74 | expect(event.snapshot.key, key); 75 | expect(event.snapshot.val()["text"], "hello"); 76 | 77 | childRef = childRef.child("text"); 78 | event = await childRef.once("value"); 79 | expect(event.snapshot.key, "text"); 80 | expect(event.snapshot.val(), "hello"); 81 | }); 82 | 83 | test("key", () { 84 | var childRef = ref.child(key); 85 | expect(key, childRef.key); 86 | }); 87 | 88 | test("parent", () { 89 | var childRef = ref.child("text"); 90 | expect(childRef.parent.toString(), ref.toString()); 91 | }); 92 | 93 | test("root", () { 94 | var childRef = ref.child("text"); 95 | expect(childRef.root.toString(), contains(databaseUrl)); 96 | }); 97 | 98 | test("empty push and set", () async { 99 | var childRef = ref.push(); 100 | expect(childRef.key, isNotNull); 101 | childRef.set({"text": "ahoj"}); 102 | 103 | var event = await childRef.once("value"); 104 | expect(event.snapshot.val()["text"], "ahoj"); 105 | }); 106 | 107 | test("wrong value push", () { 108 | expect(() => ref.push(new _TestClass()), throwsArgumentError); 109 | }); 110 | 111 | test("transaction", () async { 112 | var childRef = ref.child("todos"); 113 | childRef.set("Cooking"); 114 | 115 | await childRef 116 | .transaction((currentValue) => "$currentValue delicious dinner!"); 117 | 118 | var event = await childRef.once("value"); 119 | var val = event.snapshot.val(); 120 | expect(val, isNot("Cooking")); 121 | expect(val, "Cooking delicious dinner!"); 122 | }); 123 | 124 | test("onValue", () async { 125 | var childRef = ref.child("todos"); 126 | childRef.set(["Programming", "Cooking", "Walking with dog"]); 127 | 128 | var subscription = childRef.onValue.listen(expectAsync1((event) { 129 | var todos = event.snapshot.val(); 130 | expect(todos, isNotNull); 131 | expect(todos.length, 3); 132 | expect(todos, contains("Programming")); 133 | }, count: 1)); 134 | 135 | await subscription.cancel(); 136 | }); 137 | 138 | test("onChildAdded", () async { 139 | var childRef = ref.child("todos"); 140 | 141 | var todos = []; 142 | var eventsCount = 0; 143 | var subscription = childRef.onChildAdded.listen(expectAsync1((event) { 144 | var val = event.snapshot.val(); 145 | todos.add(val); 146 | eventsCount++; 147 | expect(eventsCount, isNonZero); 148 | expect(eventsCount, lessThan(4)); 149 | expect(val, anyOf("Programming", "Cooking", "Walking with dog")); 150 | }, count: 3)); 151 | 152 | childRef.push("Programming"); 153 | childRef.push("Cooking"); 154 | childRef.push("Walking with dog"); 155 | 156 | await subscription.cancel(); 157 | }); 158 | 159 | test("onChildRemoved", () async { 160 | var childRef = ref.child("todos"); 161 | var childKey = childRef.push("Programming").key; 162 | childRef.push("Cooking"); 163 | childRef.push("Walking with dog"); 164 | 165 | var subscription = childRef.onChildRemoved.listen(expectAsync1((event) { 166 | var val = event.snapshot.val(); 167 | expect(val, "Programming"); 168 | expect(val, isNot("Cooking")); 169 | }, count: 1)); 170 | 171 | childRef.child(childKey).remove(); 172 | await subscription.cancel(); 173 | }); 174 | 175 | test("onChildChanged", () async { 176 | var childRef = ref.child("todos"); 177 | var childKey = childRef.push("Programming").key; 178 | childRef.push("Cooking"); 179 | childRef.push("Walking with dog"); 180 | 181 | var subscription = childRef.onChildChanged.listen(expectAsync1((event) { 182 | var val = event.snapshot.val(); 183 | expect(val, "Programming a Firebase lib"); 184 | expect(val, isNot("Programming")); 185 | expect(val, isNot("Cooking")); 186 | }, count: 1)); 187 | 188 | childRef.child(childKey).set("Programming a Firebase lib"); 189 | await subscription.cancel(); 190 | }); 191 | 192 | test("onChildMoved", () async { 193 | var childRef = ref.child("todos"); 194 | var childPushRef = childRef.push("Programming"); 195 | childPushRef.setPriority(5); 196 | childRef.push("Cooking").setPriority(10); 197 | childRef.push("Walking with dog").setPriority(15); 198 | 199 | var subscription = 200 | childRef.orderByPriority().onChildMoved.listen(expectAsync1((event) { 201 | var val = event.snapshot.val(); 202 | expect(val, "Programming"); 203 | expect(val, isNot("Cooking")); 204 | }, count: 1)); 205 | 206 | childPushRef.setPriority(100); 207 | await subscription.cancel(); 208 | }); 209 | 210 | test("endAt", () async { 211 | var childRef = ref.child("flowers"); 212 | childRef.push("rose"); 213 | childRef.push("tulip"); 214 | childRef.push("chicory"); 215 | childRef.push("sunflower"); 216 | 217 | var event = await childRef.orderByValue().endAt("rose").once("value"); 218 | var flowers = []; 219 | event.snapshot.forEach((snapshot) { 220 | flowers.add(snapshot.val()); 221 | }); 222 | 223 | expect(flowers.length, 2); 224 | expect(flowers.contains("chicory"), isTrue); 225 | expect(flowers.contains("sunflower"), isFalse); 226 | }); 227 | 228 | test("endAt with wrong parameter", () { 229 | var childRef = ref.child("flowers"); 230 | childRef.push({"name": "rose"}); 231 | 232 | expect(() => childRef.orderByValue().endAt({"name": "rose"}), throws); 233 | }); 234 | 235 | test("startAt", () async { 236 | var childRef = ref.child("flowers"); 237 | childRef.push("rose"); 238 | childRef.push("tulip"); 239 | childRef.push("chicory"); 240 | childRef.push("sunflower"); 241 | 242 | var event = await childRef.orderByValue().startAt("rose").once("value"); 243 | var flowers = []; 244 | event.snapshot.forEach((snapshot) { 245 | flowers.add(snapshot.val()); 246 | }); 247 | 248 | expect(flowers.length, 3); 249 | expect(flowers.contains("sunflower"), isTrue); 250 | expect(flowers.contains("chicory"), isFalse); 251 | }); 252 | 253 | test("startAt with wrong parameter", () { 254 | var childRef = ref.child("flowers"); 255 | childRef.push({"name": "chicory"}); 256 | 257 | expect( 258 | () => childRef.orderByValue().startAt({"name": "chicory"}), throws); 259 | }); 260 | 261 | test("equalTo", () async { 262 | var childRef = ref.child("flowers"); 263 | childRef.push("rose"); 264 | childRef.push("tulip"); 265 | 266 | var event = await childRef.orderByValue().equalTo("rose").once("value"); 267 | var flowers = []; 268 | event.snapshot.forEach((snapshot) { 269 | flowers.add(snapshot.val()); 270 | }); 271 | 272 | expect(flowers, isNotNull); 273 | expect(flowers.length, 1); 274 | expect(flowers.first, "rose"); 275 | }); 276 | 277 | test("equalTo with wrong parameter", () async { 278 | var childRef = ref.child("flowers"); 279 | childRef.push({"name": "sunflower"}); 280 | 281 | expect(() => childRef.orderByValue().equalTo({"name": "sunflower"}), 282 | throws); 283 | }); 284 | 285 | test("isEqual", () async { 286 | var childRef = ref.child("flowers"); 287 | childRef.push("rose"); 288 | childRef.push("tulip"); 289 | childRef.push("chicory"); 290 | childRef.push("sunflower"); 291 | 292 | expect(ref.isEqual(childRef), isFalse); 293 | expect(ref.child("flowers").isEqual(childRef), isTrue); 294 | expect(childRef.parent.isEqual(ref), isTrue); 295 | 296 | var childQuery = childRef.limitToFirst(2); 297 | expect(childRef.limitToFirst(2).isEqual(childQuery), isTrue); 298 | expect(childRef.limitToLast(2).isEqual(childQuery), isFalse); 299 | expect(childRef.orderByValue().limitToFirst(2).isEqual(childQuery), 300 | isFalse); 301 | }); 302 | 303 | test("limitToFirst", () async { 304 | var childRef = ref.child("flowers"); 305 | childRef.push("rose"); 306 | childRef.push("tulip"); 307 | childRef.push("chicory"); 308 | childRef.push("sunflower"); 309 | 310 | var event = await childRef.orderByValue().limitToFirst(2).once("value"); 311 | var flowers = []; 312 | event.snapshot.forEach((snapshot) { 313 | flowers.add(snapshot.val()); 314 | }); 315 | 316 | expect(flowers, isNotEmpty); 317 | expect(flowers.length, 2); 318 | expect(flowers, contains("chicory")); 319 | expect(flowers, contains("rose")); 320 | }); 321 | 322 | test("limitToLast", () async { 323 | var childRef = ref.child("flowers"); 324 | childRef.push("rose"); 325 | childRef.push("tulip"); 326 | childRef.push("chicory"); 327 | childRef.push("sunflower"); 328 | 329 | var event = await childRef.orderByValue().limitToLast(1).once("value"); 330 | var flowers = []; 331 | event.snapshot.forEach((snapshot) { 332 | flowers.add(snapshot.val()); 333 | }); 334 | 335 | expect(flowers, isNotEmpty); 336 | expect(flowers.length, 1); 337 | expect(flowers, contains("tulip")); 338 | }); 339 | 340 | test("orderByKey", () async { 341 | var childRef = ref.child("flowers"); 342 | childRef.child("one").set("rose"); 343 | childRef.child("two").set("tulip"); 344 | childRef.child("three").set("chicory"); 345 | childRef.child("four").set("sunflower"); 346 | 347 | var event = await childRef.orderByKey().once("value"); 348 | var flowers = []; 349 | event.snapshot.forEach((snapshot) { 350 | flowers.add(snapshot.key); 351 | }); 352 | 353 | expect(flowers, isNotEmpty); 354 | expect(flowers.length, 4); 355 | expect(flowers, ["four", "one", "three", "two"]); 356 | }); 357 | 358 | test("orderByValue", () async { 359 | var childRef = ref.child("flowers"); 360 | childRef.push("rose"); 361 | childRef.push("tulip"); 362 | childRef.push("chicory"); 363 | childRef.push("sunflower"); 364 | 365 | var event = await childRef.orderByValue().once("value"); 366 | var flowers = []; 367 | event.snapshot.forEach((snapshot) { 368 | flowers.add(snapshot.val()); 369 | }); 370 | 371 | expect(flowers, isNotEmpty); 372 | expect(flowers.length, 4); 373 | expect(flowers, ["chicory", "rose", "sunflower", "tulip"]); 374 | }); 375 | 376 | test("orderByChild", () async { 377 | var childRef = ref.child("people"); 378 | childRef.push({"name": "Alex", "age": 27}); 379 | childRef.push({"name": "Andrew", "age": 43}); 380 | childRef.push({"name": "James", "age": 12}); 381 | 382 | var event = await childRef.orderByChild("age").once("value"); 383 | var people = []; 384 | event.snapshot.forEach((snapshot) { 385 | people.add(snapshot.val()); 386 | }); 387 | 388 | expect(people, isNotEmpty); 389 | expect(people.first["name"], "James"); 390 | expect(people.last["name"], "Andrew"); 391 | }); 392 | 393 | test("orderByPriority", () async { 394 | var childRef = ref.child("people"); 395 | childRef.child("one").setWithPriority({"name": "Alex", "age": 27}, 10); 396 | childRef.child("two").setWithPriority({"name": "Andrew", "age": 43}, 5); 397 | childRef 398 | .child("three") 399 | .setWithPriority({"name": "James", "age": 12}, 700); 400 | 401 | var event = await childRef.orderByPriority().once("value"); 402 | var people = []; 403 | event.snapshot.forEach((snapshot) { 404 | people.add(snapshot.val()); 405 | }); 406 | 407 | expect(people, isNotEmpty); 408 | expect(people.first["name"], "Andrew"); 409 | expect(people.last["name"], "James"); 410 | }); 411 | 412 | test("set with priority", () async { 413 | var childRef = ref.child("people"); 414 | childRef.child("one").setWithPriority({"name": "Alex", "age": 27}, 1.0); 415 | childRef 416 | .child("two") 417 | .setWithPriority({"name": "Andrew", "age": 43}, "A"); 418 | childRef 419 | .child("three") 420 | .setWithPriority({"name": "James", "age": 12}, null); 421 | 422 | var event = await childRef.once("value"); 423 | var priorities = []; 424 | event.snapshot.forEach((snapshot) { 425 | priorities.add(snapshot.getPriority()); 426 | }); 427 | 428 | expect(priorities, isNotEmpty); 429 | expect(priorities.contains(1.0), isTrue); 430 | expect(priorities.contains("A"), isTrue); 431 | expect(priorities.contains(null), isTrue); 432 | }); 433 | 434 | test("set with wrong priority type", () { 435 | var childRef = ref.child("people"); 436 | 437 | expect( 438 | () => childRef 439 | .child("one") 440 | .setWithPriority({"name": "Alex", "age": 27}, {"priority": 10}), 441 | throws); 442 | expect( 443 | () => childRef 444 | .child("two") 445 | .setWithPriority({"name": "Andrew", "age": 43}, true), 446 | throws); 447 | }); 448 | }); 449 | }); 450 | } 451 | 452 | class _TestClass {} 453 | -------------------------------------------------------------------------------- /test/database_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Database Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/io_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('vm') 2 | import 'package:firebase3/firebase_io.dart'; 3 | import 'package:firebase3/src/assets/assets_io.dart'; 4 | import 'package:http/http.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import 'test_util.dart'; 8 | 9 | void main() { 10 | String databaseUri; 11 | FirebaseClient fbClient; 12 | String testUri; 13 | 14 | setUpAll(() async { 15 | var token = await getAccessToken(new IOClient()); 16 | fbClient = new FirebaseClient(token.data); 17 | databaseUri = await getDatabaseUri(); 18 | }); 19 | 20 | setUp(() { 21 | var path = validDatePath(); 22 | testUri = "$databaseUri/$path.json"; 23 | }); 24 | 25 | tearDown(() async { 26 | await fbClient.delete(testUri); 27 | testUri = null; 28 | }); 29 | 30 | tearDownAll(() { 31 | if (fbClient != null) { 32 | fbClient.close(); 33 | } 34 | }); 35 | 36 | test("never-accessed path is null", () async { 37 | var response = await fbClient.get(testUri); 38 | expect(response, isNull); 39 | }); 40 | 41 | test("Uri is fine, too", () async { 42 | var response = await fbClient.get(Uri.parse(testUri)); 43 | expect(response, isNull); 44 | }); 45 | 46 | test("put", () async { 47 | var response = await fbClient.put(testUri, 'bob'); 48 | expect(response, 'bob'); 49 | 50 | response = await fbClient.get(testUri); 51 | expect(response, 'bob'); 52 | }); 53 | 54 | test("post", () async { 55 | var response = (await fbClient.post(testUri, 'bob')) as Map; 56 | expect(response, contains('name')); 57 | 58 | var key = response['name']; 59 | 60 | response = await fbClient.get(testUri); 61 | expect(response, {key: 'bob'}); 62 | }); 63 | 64 | test("patch", () async { 65 | var response = (await fbClient.patch(testUri, {'someNewKey': 'bob'})) as Map; 66 | expect(response, contains('someNewKey')); 67 | 68 | var key = 'someNewKey'; 69 | 70 | response = await fbClient.get(testUri); 71 | expect(response, {key: 'bob'}); 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /test/storage_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('browser') 2 | import 'dart:convert'; 3 | 4 | import 'package:firebase3/firebase.dart'; 5 | import 'package:firebase3/src/assets/assets.dart'; 6 | import 'package:path/path.dart' as p; 7 | import 'package:test/test.dart'; 8 | 9 | import 'test_util.dart'; 10 | 11 | void main() { 12 | App app; 13 | 14 | setUpAll(() async { 15 | await config(); 16 | }); 17 | 18 | setUp(() async { 19 | app = initializeApp( 20 | apiKey: apiKey, 21 | authDomain: authDomain, 22 | databaseURL: databaseUrl, 23 | storageBucket: storageBucket); 24 | }); 25 | 26 | tearDown(() async { 27 | if (app != null) { 28 | await app.delete(); 29 | app = null; 30 | } 31 | }); 32 | 33 | group('Reference', () { 34 | final pathPrefix = validDatePath(); 35 | final fileName = 'storage_test.json'; 36 | final filePath = p.join(pathPrefix, fileName); 37 | 38 | StorageReference ref; 39 | 40 | setUp(() async { 41 | var storage = app.storage(); 42 | 43 | ref = storage.ref(filePath); 44 | var metadata = new UploadMetadata( 45 | contentType: r'application/json', 46 | customMetadata: {'the answer': '42'}); 47 | var bytes = new JsonUtf8Encoder().convert([1, 2, 3]); 48 | 49 | var upload = ref.put(bytes, metadata); 50 | var snapShot = await upload.future; 51 | 52 | expect(snapShot.bytesTransferred, 7); 53 | expect(snapShot.downloadURL.pathSegments.last, contains(fileName)); 54 | expect(snapShot.state, TaskState.SUCCESS); 55 | 56 | var md = snapShot.metadata; 57 | expect(md.bucket, storageBucket); 58 | expect(md.name, fileName); 59 | expect(md.fullPath, filePath); 60 | expect(md.size, 7); 61 | expect(md.contentType, 'application/json'); 62 | expect(md.timeCreated, md.updated); 63 | expect(md.downloadURLs.single.pathSegments.last, contains(fileName)); 64 | expect(md.customMetadata, isNotNull); 65 | expect(md.customMetadata['the answer'], '42'); 66 | }); 67 | 68 | tearDown(() async { 69 | await ref.delete(); 70 | ref = null; 71 | }); 72 | 73 | test('getDownloadURL', () async { 74 | var downloadUrl = await ref.getDownloadURL(); 75 | 76 | expect(downloadUrl.toString(), contains(storageBucket)); 77 | expect(downloadUrl.pathSegments.last, contains(filePath)); 78 | }); 79 | 80 | test('getMetadata', () async { 81 | var md = await ref.getMetadata(); 82 | 83 | expect(md.bucket, storageBucket); 84 | expect(md.name, fileName); 85 | expect(md.fullPath, filePath); 86 | expect(md.size, 7); 87 | expect(md.contentType, 'application/json'); 88 | expect(md.timeCreated, md.updated); 89 | expect(md.downloadURLs.single.pathSegments.last, contains(fileName)); 90 | expect(md.customMetadata, isNotNull); 91 | expect(md.customMetadata['the answer'], '42'); 92 | }); 93 | 94 | test('updateMetadata', () async { 95 | var newMetadata = new SettableMetadata(contentType: 'text/plain'); 96 | 97 | var md = await ref.updateMetadata(newMetadata); 98 | 99 | expect(md.bucket, storageBucket); 100 | expect(md.name, fileName); 101 | expect(md.fullPath, filePath); 102 | expect(md.size, 7); 103 | expect(md.contentType, 'text/plain'); 104 | expect(md.updated.isAfter(md.timeCreated), isTrue); 105 | expect(md.downloadURLs.single.pathSegments.last, contains(fileName)); 106 | expect(md.customMetadata, isNull); 107 | }); 108 | }); 109 | } 110 | -------------------------------------------------------------------------------- /test/storage_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Storage Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/test_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:path/path.dart' as p; 2 | 3 | String validDatePath() => p.join('pkg_firebase3_test', 4 | new DateTime.now().toUtc().toIso8601String().replaceAll('.', '_')); 5 | 6 | printException(e) => print( 7 | [e.name, e.code, e.message, e.stack].where((s) => s != null).join('\n')); 8 | -------------------------------------------------------------------------------- /test/utils_test.dart: -------------------------------------------------------------------------------- 1 | @TestOn('browser') 2 | import 'package:firebase3/src/utils.dart'; 3 | 4 | import 'package:test/test.dart'; 5 | 6 | void _testRoundTrip(Object value) { 7 | var js = jsify(value); 8 | var roundTrip = dartify(js); 9 | expect(roundTrip, value); 10 | } 11 | 12 | void main() { 13 | group('jsify and dartify', () { 14 | group('basic objects', () { 15 | var jsonObjects = { 16 | 'int': 0, 17 | 'null': null, 18 | 'string': 'string', 19 | 'bool': true, 20 | 'double': 1.1, 21 | 'list': [1, 2, 3], 22 | 'map': {'a': true} 23 | }; 24 | 25 | jsonObjects.forEach((key, value) { 26 | test(key, () => _testRoundTrip(value)); 27 | }); 28 | }); 29 | 30 | test('custom object', () { 31 | expect(() => jsify(new _TestClass()), throwsArgumentError); 32 | }); 33 | 34 | test('custom object with toJson', () { 35 | expect(() => jsify(new _TestClassWithToJson()), throwsArgumentError); 36 | }); 37 | }); 38 | } 39 | 40 | class _TestClass {} 41 | 42 | class _TestClassWithToJson { 43 | Object toJson() => const {}; 44 | } 45 | -------------------------------------------------------------------------------- /tool/create_config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:path/path.dart' as p; 5 | 6 | final _assetPath = 'lib/src/assets/'; 7 | 8 | main() { 9 | // make sure the working dir is the root of the project 10 | if (!(new File('pubspec.yaml').existsSync())) { 11 | throw new StateError("Not in the root! - ${Directory.current}"); 12 | } 13 | 14 | var samplePath = p.join(_assetPath, 'config.json.sample'); 15 | if (!(new File(samplePath).existsSync())) { 16 | throw new StateError("'$samplePath should exist"); 17 | } 18 | 19 | var configPath = p.join(_assetPath, 'config.json'); 20 | var configFile = new File(configPath); 21 | 22 | if (configFile.existsSync()) { 23 | throw new StateError("Config exists. It should not. '$configPath'"); 24 | } 25 | 26 | var vars = ["API_KEY", "AUTH_DOMAIN", "DATABASE_URL", "STORAGE_BUCKET"]; 27 | 28 | var config = {}; 29 | for (var envVar in vars) { 30 | if (Platform.environment.containsKey(envVar)) { 31 | config[envVar] = Platform.environment[envVar]; 32 | } else { 33 | throw new StateError('Missing needed environment variable $envVar'); 34 | } 35 | } 36 | 37 | configFile 38 | .writeAsStringSync(new JsonEncoder.withIndent(' ').convert(config)); 39 | 40 | // now for the service_account silly 41 | if (!Platform.environment.containsKey('SERVICE_ACCOUNT_JSON')) { 42 | throw new StateError('Expected a ENV variable of SERVICE_ACCOUNT_JSON'); 43 | } 44 | 45 | var serviceAccountJson = 46 | UTF8.decode(BASE64.decode(Platform.environment['SERVICE_ACCOUNT_JSON'])); 47 | 48 | var serviceAccountPath = p.join(_assetPath, 'service_account.json'); 49 | new File(serviceAccountPath).writeAsStringSync(serviceAccountJson); 50 | } 51 | -------------------------------------------------------------------------------- /tool/travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fast fail the script on failures. 4 | set -e 5 | 6 | if [ -n "$API_KEY" ]; then 7 | dart tool/create_config.dart 8 | THE_COMMAND="pub run test -p $TEST_PLATFORM" 9 | echo $THE_COMMAND 10 | exec $THE_COMMAND 11 | else 12 | echo 'Missing firebase ENV variables.' 13 | fi 14 | --------------------------------------------------------------------------------