Skip to content

Commit

Permalink
feat: firestore
Browse files Browse the repository at this point in the history
  • Loading branch information
BreX900 committed Apr 27, 2023
1 parent 598edc8 commit 967f3f3
Show file tree
Hide file tree
Showing 14 changed files with 762 additions and 3 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ build/
# Directory created by dartdoc
doc/api/

*.iml
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Currently, only supports admin methods for the following firebase services:

* authentication
* realtime database
* cloud firestore

## Example using service account

Expand Down
7 changes: 6 additions & 1 deletion lib/firebase_admin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@ library firebase_admin;
export 'src/admin.dart';
export 'src/app.dart' hide AppInternalsExtension;
export 'src/auth.dart';
export 'src/utils/error.dart';
export 'src/credential.dart' hide setApplicationDefaultCredential;
export 'src/firestore/collection.dart';
export 'src/firestore/document.dart';
export 'src/firestore/firestore.dart';
export 'src/firestore/query.dart';
export 'src/firestore/transaction.dart';
export 'src/utils/error.dart';
3 changes: 3 additions & 0 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class App {
/// Gets [Storage] service for this application.
Storage storage() => _getService(() => Storage(this));

/// Gets [Firestore] service for this application.
Firestore firestore() => _getService(() => Firestore(this));

/// Renders this app unusable and frees the resources of all associated
/// services.
Future<void> delete() async {
Expand Down
45 changes: 45 additions & 0 deletions lib/src/firestore/collection.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import 'package:firebase_admin/src/firestore/document.dart';
import 'package:firebase_admin/src/firestore/firestore.dart';
import 'package:firebase_admin/src/firestore/query.dart';
import 'package:firebase_admin/src/firestore/utils/auto_id_generator.dart';
import 'package:firebase_admin/src/firestore/utils/pointer.dart';

class CollectionReference<T> extends Query<T> {
final Pointer _pointer;

CollectionReference({
required super.firestore,
required super.toFirestore,
required super.fromFirestore,
required super.path,
}) : _pointer = Pointer(path),
super(query: null) {
assert(_pointer.isCollection());
}

DocumentReference<T> doc([String? id]) {
return DocumentReference(
firestore: firestore,
toFirestore: toFirestore,
fromFirestore: fromFirestore,
path: _pointer.documentPath(id ?? AutoIdGenerator.autoId()),
);
}

Future<DocumentSnapshot<T>> add(T data) async {
final document = doc();
return await document.set(data);
}

CollectionReference<R> withConverter<R>({
required FromFirestore<R> fromFirestore,
required ToFirestore<R> toFirestore,
}) {
return CollectionReference<R>(
firestore: firestore,
path: path,
fromFirestore: fromFirestore,
toFirestore: toFirestore,
);
}
}
92 changes: 92 additions & 0 deletions lib/src/firestore/document.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:firebase_admin/src/firestore/firestore.dart';
import 'package:firebase_admin/src/firestore/utils/document_snapshot.dart';
import 'package:firebase_admin/src/firestore/utils/pointer.dart';
import 'package:firebase_admin/src/firestore/utils/serialization.dart';
import 'package:googleapis/firestore/v1.dart';

class DocumentReference<T> {
final Firestore firestore;
final ToFirestore<T> toFirestore;
final FromFirestore<T> fromFirestore;
final Pointer _pointer;
String get path => _pointer.path;
String get id => _pointer.id;

DocumentReference({
required this.firestore,
required this.toFirestore,
required this.fromFirestore,
required String path,
}) : _pointer = Pointer(path) {
assert(_pointer.isDocument());
}

Future<DocumentSnapshot<T>> set(T data) async {
final result = await firestore.docsApi.createDocument(
Document(fields: serializeData(toFirestore(data))),
firestore.databasePath,
_pointer.parentPath()!,
documentId: _pointer.id,
);

return SerializableDocumentSnapshot(
firestore: firestore,
toFirestore: toFirestore,
fromFirestore: fromFirestore,
document: result,
);
}

Future<DocumentSnapshot<T>> get() async {
final result = await firestore.docsApi.get('${firestore.databasePath}/$path');

return SerializableDocumentSnapshot(
firestore: firestore,
toFirestore: toFirestore,
fromFirestore: fromFirestore,
document: result,
);
}

Future<DocumentSnapshot<T>> update(Map<String, dynamic> data) async {
final result = await firestore.docsApi.patch(
Document(fields: serializeData(data)),
'${firestore.databasePath}/${_pointer.path}',
);

return SerializableDocumentSnapshot(
firestore: firestore,
toFirestore: toFirestore,
fromFirestore: fromFirestore,
document: result,
);
}

Future<void> delete() async {
await firestore.docsApi.delete('${firestore.databasePath}/$path');
}

DocumentReference<R> withConverter<R>({
required FromFirestore<R> fromFirestore,
required ToFirestore<R> toFirestore,
}) {
return DocumentReference<R>(
firestore: firestore,
path: path,
fromFirestore: fromFirestore,
toFirestore: toFirestore,
);
}
}

abstract class DocumentSnapshot<T> {
final Firestore firestore;

const DocumentSnapshot({
required this.firestore,
});

DocumentReference<T> get reference;

T data();
}
60 changes: 60 additions & 0 deletions lib/src/firestore/firestore.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import 'package:firebase_admin/src/app.dart';
import 'package:firebase_admin/src/app/app_extension.dart';
import 'package:firebase_admin/src/firestore/collection.dart';
import 'package:firebase_admin/src/firestore/document.dart';
import 'package:firebase_admin/src/firestore/transaction.dart';
import 'package:firebase_admin/src/firestore/utils/serialization.dart';
import 'package:firebase_admin/src/service.dart';
import 'package:firebase_admin/src/utils/api_request.dart';
import 'package:googleapis/firestore/v1.dart';
import 'package:meta/meta.dart';

typedef ToFirestore<T> = Map<String, dynamic> Function(T value);
typedef FromFirestore<T> = T Function(DocumentSnapshot<Map<String, dynamic>> snapshot);

class Firestore implements FirebaseService {
@override
final App app;

final FirestoreApi _api;

@internal
ProjectsDatabasesDocumentsResource get docsApi => _api.projects.databases.documents;

@internal
String get databasePath => 'projects/${app.projectId}/databases/(default)/documents';

Firestore(this.app) : _api = FirestoreApi(AuthorizedHttpClient(app));

@override
Future<void> delete() async {}

CollectionReference<Map<String, dynamic>> collection(String id) {
return CollectionReference(
firestore: this,
fromFirestore: fromFirestore,
toFirestore: toFirestore,
path: id,
);
}

DocumentReference<Map<String, dynamic>> doc(String id) {
return DocumentReference(
firestore: this,
fromFirestore: fromFirestore,
toFirestore: toFirestore,
path: id,
);
}

Future<T> runTransaction<T>(
Future<T> Function(Transaction transaction) handler, {
Duration timeout = const Duration(seconds: 30),
}) {
return Transaction.run(
firestore: this,
timeout: timeout,
handler: handler,
);
}
}
Loading

0 comments on commit 967f3f3

Please sign in to comment.