adding packages
This commit is contained in:
Executable
+78
@@ -0,0 +1,78 @@
|
||||
part of dart_firebase;
|
||||
|
||||
abstract class FirestoreClient {
|
||||
factory FirestoreClient(String email, String password, App? app,
|
||||
{FirestoreApiEndpoints? endpoints, FirestoreAccessToken? token}) {
|
||||
return new FirestoreClientImpl(email, password, app, token,
|
||||
endpoints == null ? new FirestoreApiEndpoints.standard() : endpoints);
|
||||
}
|
||||
|
||||
String get email;
|
||||
String get password;
|
||||
|
||||
App get app;
|
||||
|
||||
FirestoreAccessToken get token;
|
||||
set token(FirestoreAccessToken token);
|
||||
|
||||
bool get isAuthorized;
|
||||
|
||||
FirestoreApiEndpoints get endpoints;
|
||||
|
||||
Future login();
|
||||
|
||||
Future<List<DocumentSnapshot>> listDocumentSnapshots(String path);
|
||||
Future<DocumentSnapshot> getDocumentSnapshot(String path);
|
||||
|
||||
/// Gets a [CollectionReference] for the specified Firestore path.
|
||||
CollectionReference collection(String path);
|
||||
|
||||
/// Gets a [DocumentReference] for the specified Firestore path.
|
||||
DocumentReference document(String path);
|
||||
|
||||
Future close();
|
||||
}
|
||||
|
||||
const String _defaultAppName = "default";
|
||||
|
||||
class App {
|
||||
/// Creates (and initializes) a Firebase App with API key, auth domain,
|
||||
/// database URL and storage bucket.
|
||||
///
|
||||
/// See: <https://firebase.google.com/docs/reference/js/firebase#.initializeApp>.
|
||||
const App({
|
||||
required this.apiKey,
|
||||
this.authDomain,
|
||||
this.databaseURL,
|
||||
required this.projectId,
|
||||
this.storageBucket,
|
||||
this.messagingSenderId,
|
||||
this.appId,
|
||||
String? database,
|
||||
}) : _name = database,
|
||||
assert(apiKey != null),
|
||||
assert(projectId != null);
|
||||
|
||||
factory App.fromJson(Map<String, dynamic> json) {
|
||||
return App(
|
||||
apiKey: json['apiKey'],
|
||||
authDomain: json['authDomain'],
|
||||
databaseURL: json['databaseURL'],
|
||||
projectId: json['projectId'],
|
||||
storageBucket: json['storageBucket'],
|
||||
messagingSenderId: json['messagingSenderId'],
|
||||
appId: json['appId'],
|
||||
);
|
||||
}
|
||||
final String apiKey;
|
||||
final String? authDomain;
|
||||
final String? databaseURL;
|
||||
final String projectId;
|
||||
final String? storageBucket;
|
||||
final String? messagingSenderId;
|
||||
final String? appId;
|
||||
|
||||
/// Database Override [DEFAULT]
|
||||
String get name => _name ?? _defaultAppName;
|
||||
final String? _name;
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
part of dart_firebase;
|
||||
|
||||
abstract class FirestoreApiEndpoints {
|
||||
factory FirestoreApiEndpoints.standard() {
|
||||
return new FirestoreStandardApiEndpoints();
|
||||
}
|
||||
|
||||
Uri getFirestoreUrl(App app);
|
||||
Uri getAuthUrl(App app);
|
||||
Uri getRefreshUrl(App app);
|
||||
bool get enableProxyMode;
|
||||
}
|
||||
|
||||
class FirestoreStandardApiEndpoints implements FirestoreApiEndpoints {
|
||||
@override
|
||||
Uri getFirestoreUrl(App app) => Uri.parse(
|
||||
"https://firestore.googleapis.com/v1/projects/${app.projectId}/databases/(${app.name})/documents/");
|
||||
|
||||
@override
|
||||
bool get enableProxyMode => false;
|
||||
|
||||
@override
|
||||
Uri getAuthUrl(App app) => Uri.parse(
|
||||
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=${app.apiKey}');
|
||||
|
||||
@override
|
||||
Uri getRefreshUrl(App app) => Uri.parse(
|
||||
'https://securetoken.googleapis.com/v1/token?key=${app.apiKey}');
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
library dart_firebase.impl.browser;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:html';
|
||||
|
||||
import '../../api.dart';
|
||||
import 'common/http.dart';
|
||||
|
||||
class FirestoreClientImpl extends FirestoreHttpClient {
|
||||
FirestoreClientImpl(String email, String password, App app,
|
||||
FirestoreAccessToken token, FirestoreApiEndpoints endpoints)
|
||||
: super(email, password, app, token, endpoints);
|
||||
|
||||
@override
|
||||
Future<dynamic> sendHttpRequest(Uri uri,
|
||||
{bool needsToken: true,
|
||||
String? extract,
|
||||
Map<String, dynamic>? body}) async {
|
||||
var request = new HttpRequest();
|
||||
request.open(body == null ? "GET" : "POST", uri.toString());
|
||||
if (needsToken) {
|
||||
if (!isCurrentTokenValid(true)) {
|
||||
await login();
|
||||
}
|
||||
request.setRequestHeader("Authorization", "Bearer ${token.accessToken}");
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
request.setRequestHeader(
|
||||
"Content-Type", "application/json; charset=utf-8");
|
||||
request.send(const JsonEncoder().convert(body));
|
||||
} else {
|
||||
request.send();
|
||||
}
|
||||
|
||||
await request.onLoadEnd.first;
|
||||
|
||||
var content = request.responseText!;
|
||||
if (request.status != 200) {
|
||||
throw new Exception(
|
||||
"Failed to perform action. (Status Code: ${request.status})\n${content}");
|
||||
}
|
||||
var result = const JsonDecoder().convert(content);
|
||||
|
||||
if (result is! Map) {
|
||||
if (extract != null) {
|
||||
return result[extract];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() async {}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
library dart_firebase.impl.common.http;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../api.dart';
|
||||
|
||||
const String _defaultAppName = "[DEFAULT]";
|
||||
|
||||
abstract class FirestoreHttpClient implements FirestoreClient {
|
||||
FirestoreHttpClient(
|
||||
this.email, this.password, this.app, this.token, this.endpoints);
|
||||
|
||||
@override
|
||||
final String email;
|
||||
|
||||
@override
|
||||
final String password;
|
||||
|
||||
@override
|
||||
final App app;
|
||||
|
||||
@override
|
||||
final FirestoreApiEndpoints endpoints;
|
||||
|
||||
@override
|
||||
FirestoreAccessToken token;
|
||||
|
||||
bool isCurrentTokenValid(bool refreshable) {
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refreshable) {
|
||||
var now = DateTime.now();
|
||||
return token.expiresAt.difference(now).abs().inSeconds >= 60;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAuthorized => isCurrentTokenValid(true);
|
||||
|
||||
@override
|
||||
Future login() async {
|
||||
if (!isCurrentTokenValid(false)) {
|
||||
var result = await sendHttpRequest(endpoints.getAuthUrl(app),
|
||||
body: {
|
||||
"email": email,
|
||||
"password": password,
|
||||
"returnSecureToken": true,
|
||||
},
|
||||
needsToken: false);
|
||||
|
||||
token = FirestoreJsonAccessToken(result, DateTime.now());
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await sendHttpRequest(endpoints.getRefreshUrl(app),
|
||||
body: {
|
||||
"grant_type": "refresh_token",
|
||||
"refresh_token": token.refreshToken
|
||||
},
|
||||
needsToken: false);
|
||||
|
||||
token = FirestoreJsonAccessToken(result, DateTime.now());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DocumentSnapshot>> listDocumentSnapshots(String path) async {
|
||||
var list = <DocumentSnapshot>[];
|
||||
|
||||
var result = await (getJsonList("$path", extract: 'documents') as FutureOr<List<dynamic>>);
|
||||
|
||||
for (var item in result) {
|
||||
list.add(new DocumentSnapshot(this, item));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@override
|
||||
CollectionReference collection(String path) {
|
||||
assert(path != null);
|
||||
return CollectionReference(this, path.split('/'));
|
||||
}
|
||||
|
||||
@override
|
||||
DocumentReference document(String path) {
|
||||
assert(path != null);
|
||||
return DocumentReference(this, path.split('/'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DocumentSnapshot> getDocumentSnapshot(String path) async {
|
||||
final _data = await getJsonMap("$path", extract: null);
|
||||
return DocumentSnapshot(this, _asStringKeyedMap(_data));
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getJsonMap(String url,
|
||||
{Map<String, dynamic>? body,
|
||||
String? extract: "response",
|
||||
bool standard: true}) async {
|
||||
return (await sendHttpRequest(_apiUrl(url, standard),
|
||||
body: body, extract: extract)) as Map<String, dynamic>?;
|
||||
}
|
||||
|
||||
Future<List<dynamic>?> getJsonList(String url,
|
||||
{Map<String, dynamic>? body,
|
||||
String extract: "response",
|
||||
bool standard: true}) async {
|
||||
return (await sendHttpRequest(_apiUrl(url, standard),
|
||||
body: body, extract: extract)) as List<dynamic>?;
|
||||
}
|
||||
|
||||
Future<dynamic> sendHttpRequest(Uri uri,
|
||||
{bool needsToken: true, String? extract, Map<String, dynamic>? body});
|
||||
|
||||
Uri _apiUrl(String path, bool standard) {
|
||||
path = standard ? "$path" : path;
|
||||
var uri = endpoints.getFirestoreUrl(app).resolve(path);
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic>? _asStringKeyedMap(Map<dynamic, dynamic>? map) {
|
||||
if (map == null) return null;
|
||||
if (map is Map<String, dynamic>) {
|
||||
return map;
|
||||
} else {
|
||||
return Map<String, dynamic>.from(map);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
library dart_firebase.impl.io;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../../api.dart';
|
||||
import 'common/http.dart';
|
||||
|
||||
final ContentType _jsonContentType =
|
||||
new ContentType("application", "json", charset: "utf-8");
|
||||
|
||||
HttpClient _createHttpClient() {
|
||||
var client = new HttpClient();
|
||||
client.userAgent = "Firestore.dart";
|
||||
return client;
|
||||
}
|
||||
|
||||
class FirestoreClientImpl extends FirestoreHttpClient {
|
||||
FirestoreClientImpl(String email, String password, App app,
|
||||
FirestoreAccessToken token, FirestoreApiEndpoints endpoints,
|
||||
{HttpClient? client})
|
||||
: this.client = client == null ? _createHttpClient() : client,
|
||||
super(email, password, app, token, endpoints);
|
||||
|
||||
final HttpClient client;
|
||||
|
||||
@override
|
||||
Future<dynamic> sendHttpRequest(Uri uri,
|
||||
{bool needsToken: true,
|
||||
String? extract,
|
||||
Map<String, dynamic>? body}) async {
|
||||
if (endpoints.enableProxyMode) {
|
||||
uri = uri.replace(queryParameters: {"__firebase": "api"});
|
||||
}
|
||||
|
||||
var request =
|
||||
body == null ? await client.getUrl(uri) : await client.postUrl(uri);
|
||||
request.headers.set("User-Agent", "Firestore.dart");
|
||||
if (needsToken) {
|
||||
if (!isCurrentTokenValid(true)) {
|
||||
await login();
|
||||
}
|
||||
request.headers.add("Authorization", "Bearer ${token.accessToken}");
|
||||
}
|
||||
if (body != null) {
|
||||
request.headers.contentType = _jsonContentType;
|
||||
request.write(const JsonEncoder().convert(body));
|
||||
}
|
||||
var response = await request.close();
|
||||
var content = await response.transform(const Utf8Decoder()).join();
|
||||
if (response.statusCode != 200) {
|
||||
throw new Exception(
|
||||
"Failed to perform action. $uri (Status Code: ${response.statusCode})\n${content}");
|
||||
}
|
||||
var result = const JsonDecoder().convert(content);
|
||||
|
||||
if (result is Map) {
|
||||
if (extract != null) {
|
||||
return result[extract];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() async {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
library dart_firebase.impl.unsupported;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import '../../api.dart';
|
||||
|
||||
class FirestoreClientImpl implements FirestoreClient {
|
||||
FirestoreClientImpl(String email, String password, App? app,
|
||||
FirestoreAccessToken? token, FirestoreApiEndpoints endpoints) {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
App get app => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
String get email => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
String get password => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
FirestoreAccessToken get token => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
set token(FirestoreAccessToken token) =>
|
||||
throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
bool get isAuthorized => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
FirestoreApiEndpoints get endpoints =>
|
||||
throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
Future<DocumentSnapshot> getDocumentSnapshot(String path) {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DocumentSnapshot>> listDocumentSnapshots(String path) {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
Future login() {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
String get apiKey => throw "This platform is not supported.";
|
||||
|
||||
@override
|
||||
CollectionReference collection(String path) {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
|
||||
@override
|
||||
DocumentReference document(String path) {
|
||||
throw "This platform is not supported.";
|
||||
}
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
part of dart_firebase;
|
||||
|
||||
abstract class FirestoreAccessToken {
|
||||
String? get accessToken;
|
||||
String? get refreshToken;
|
||||
DateTime get createdAt;
|
||||
DateTime get expiresAt;
|
||||
|
||||
bool get isExpired => expiresAt.isAfter(new DateTime.now());
|
||||
}
|
||||
|
||||
class FirestoreJsonAccessToken extends FirestoreAccessToken {
|
||||
FirestoreJsonAccessToken(this.json, this.createdAt);
|
||||
|
||||
final Map<String, dynamic>? json;
|
||||
String? get displayName => json!['displayName'] as String?;
|
||||
String? get email => json!['email'] as String?;
|
||||
String? get kind => json!['kind'] as String?;
|
||||
String? get localId => json!['localId'] as String?;
|
||||
bool? get registered => json!['registered'] as bool?;
|
||||
int? get expiresInSeconds => int.tryParse(json!["expiresIn"]);
|
||||
|
||||
@override
|
||||
String? get accessToken => json!["idToken"] as String?;
|
||||
|
||||
@override
|
||||
String? get refreshToken => json!["refresh_token"] as String?;
|
||||
|
||||
@override
|
||||
final DateTime createdAt;
|
||||
|
||||
@override
|
||||
DateTime get expiresAt =>
|
||||
createdAt.add(new Duration(seconds: expiresInSeconds!));
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
part of dart_firebase;
|
||||
|
||||
class CollectionReference implements FirestoreReference {
|
||||
CollectionReference(this.client, this.pathComponents)
|
||||
: _pathComponents = pathComponents;
|
||||
|
||||
final List<String> _pathComponents;
|
||||
|
||||
@override
|
||||
final FirestoreClient client;
|
||||
|
||||
@override
|
||||
final List<String> pathComponents;
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic o) =>
|
||||
o is DocumentReference && o.client == client && o.path == path;
|
||||
|
||||
/// ID of the referenced collection.
|
||||
String? get id => _pathComponents.isEmpty ? null : _pathComponents.last;
|
||||
|
||||
/// For subcollections, parent returns the containing DocumentReference.
|
||||
///
|
||||
/// For root collections, null is returned.
|
||||
CollectionReference? parent() {
|
||||
if (_pathComponents.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return CollectionReference(
|
||||
client,
|
||||
(List<String>.from(_pathComponents)..removeLast()),
|
||||
);
|
||||
}
|
||||
|
||||
/// Slash-delimited path representing the database location of this query.
|
||||
String get path => _pathComponents.join('/');
|
||||
|
||||
/// This document's given or generated ID in the collection.
|
||||
String get documentID => _pathComponents.last;
|
||||
|
||||
/// Returns a `DocumentReference` with the provided path.
|
||||
///
|
||||
/// If no [path] is provided, an auto-generated ID is used.
|
||||
///
|
||||
/// The unique key generated is prefixed with a client-generated timestamp
|
||||
/// so that the resulting list will be chronologically-sorted.
|
||||
DocumentReference document([String? path]) {
|
||||
List<String> childPath;
|
||||
if (path == null) {
|
||||
final String key = PushIdGenerator.generatePushChildName();
|
||||
childPath = List<String>.from(_pathComponents)..add(key);
|
||||
} else {
|
||||
childPath = List<String>.from(_pathComponents)..addAll(path.split(('/')));
|
||||
}
|
||||
return DocumentReference(client, childPath);
|
||||
}
|
||||
|
||||
Future<List<DocumentSnapshot>> snapshots() async {
|
||||
return client.listDocumentSnapshots('$path');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
part of dart_firebase;
|
||||
|
||||
class DocumentReference implements FirestoreReference {
|
||||
DocumentReference(this.client, this.pathComponents)
|
||||
: _pathComponents = pathComponents;
|
||||
|
||||
final List<String> _pathComponents;
|
||||
|
||||
@override
|
||||
final FirestoreClient client;
|
||||
|
||||
@override
|
||||
final List<String> pathComponents;
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic o) =>
|
||||
o is DocumentReference && o.client == client && o.path == path;
|
||||
|
||||
/// Slash-delimited path representing the database location of this query.
|
||||
String get path => _pathComponents.join('/');
|
||||
|
||||
/// This document's given or generated ID in the collection.
|
||||
String get documentID => _pathComponents.last;
|
||||
|
||||
/// Returns the reference of a collection contained inside of this
|
||||
/// document.
|
||||
CollectionReference collection(String collectionPath) {
|
||||
assert(collectionPath != null);
|
||||
return CollectionReference(client, <String>[path, collectionPath]);
|
||||
}
|
||||
|
||||
/// Reads the document referenced by this [DocumentReference].
|
||||
///
|
||||
/// If no document exists, the read will return null.
|
||||
Future<DocumentSnapshot> get() async {
|
||||
return client.getDocumentSnapshot('$path');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
part of dart_firebase;
|
||||
|
||||
class DocumentSnapshot implements FirestoreObject {
|
||||
DocumentSnapshot(this.client, this.json);
|
||||
|
||||
/// Reads individual values from the snapshot
|
||||
dynamic operator [](String key) => json![key];
|
||||
|
||||
@override
|
||||
final FirestoreClient client;
|
||||
|
||||
@override
|
||||
final Map<String, dynamic>? json;
|
||||
|
||||
/// Gets a [DocumentReference] for the specified Firestore path.
|
||||
DocumentReference get reference {
|
||||
assert(path != null);
|
||||
return DocumentReference(client, path!.split('/'));
|
||||
}
|
||||
|
||||
/// Returns the ID of the snapshot's document
|
||||
String get documentID => path!.split('/').last;
|
||||
|
||||
/// Returns `true` if the document exists.
|
||||
bool get exists => json != null;
|
||||
|
||||
String? get path => json!['name'];
|
||||
|
||||
DateTime? get dateCreate => json!['createTime'];
|
||||
|
||||
DateTime? get dateUpdated => json!['updateTime'];
|
||||
|
||||
Map<String, dynamic> get data => _getData(json!['fields']);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _getData(json) {
|
||||
final Map<String, dynamic> _data = {};
|
||||
for (var f in json.keys) {
|
||||
final _item = json[f];
|
||||
_data['$f'] = _getValue(_item);
|
||||
}
|
||||
return _data;
|
||||
}
|
||||
|
||||
dynamic _getValue(value) {
|
||||
final Map map = json.decode(json.encode(value));
|
||||
for (String key in map.keys as Iterable<String>) {
|
||||
final _map = json.decode(json.encode(map[key]));
|
||||
if (key == 'stringValue') {
|
||||
return _map as String?;
|
||||
} else if (key == 'nullValue') {
|
||||
return null;
|
||||
} else if (key == 'timestampValue') {
|
||||
return DateTime.tryParse(_map);
|
||||
} else if (key == 'booleanValue') {
|
||||
return _map as bool?;
|
||||
} else if (key == 'number_value') {
|
||||
return num.tryParse(_map.toString());
|
||||
} else if (key == 'geoPointValue') {
|
||||
return {
|
||||
"latitude": num.tryParse(_map['latitude'].toString()),
|
||||
"longitude": num.tryParse(_map['longitude'].toString()),
|
||||
};
|
||||
} else if (key == 'arrayValue') {
|
||||
return _getList(_map);
|
||||
} else if (key == 'mapValue') {
|
||||
return _getData(_map['fields']);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
List<dynamic> _getList(Map<String, dynamic> data) {
|
||||
final _list = List.from(data['values']);
|
||||
List<dynamic> _items = [];
|
||||
for (var item in _list) {
|
||||
_items.add(_getValue(item));
|
||||
}
|
||||
return _items;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
part of dart_firebase;
|
||||
|
||||
abstract class FirestoreReference {
|
||||
FirestoreClient get client;
|
||||
List<String> get pathComponents;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
part of dart_firebase;
|
||||
|
||||
abstract class FirestoreObject {
|
||||
FirestoreClient get client;
|
||||
Map<String, dynamic>? get json;
|
||||
}
|
||||
Reference in New Issue
Block a user