From b38fea130ffba81faf42fdcbbd96b4ffd78776b2 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 20 Apr 2022 22:57:14 +0200 Subject: [PATCH] feat(crud) : new package for crud operations --- packages/wyatt_crud_bloc/.gitignore | 10 + packages/wyatt_crud_bloc/CHANGELOG.md | 3 + packages/wyatt_crud_bloc/README.md | 39 ++++ .../wyatt_crud_bloc/analysis_options.yaml | 1 + .../lib/src/crud/builder/builder.dart | 18 ++ .../lib/src/crud/builder/crud_builder.dart | 53 +++++ .../src/crud/builder/crud_stream_builder.dart | 55 +++++ .../wyatt_crud_bloc/lib/src/crud/crud.dart | 18 ++ .../lib/src/crud/cubit/crud_cubit.dart | 219 ++++++++++++++++++ .../lib/src/crud/cubit/crud_state.dart | 60 +++++ .../wyatt_crud_bloc/lib/src/models/model.dart | 21 ++ .../lib/src/models/models.dart | 18 ++ .../lib/src/models/queries/queries.dart | 18 ++ .../src/models/queries/queries_firestore.dart | 88 +++++++ .../src/models/queries/queries_interface.dart | 57 +++++ .../crud_repository_firebase_database.dart | 102 ++++++++ .../crud_repository_firestore.dart | 146 ++++++++++++ .../crud_repository_interface.dart | 30 +++ .../lib/src/repositories/repositories.dart | 18 ++ .../wyatt_crud_bloc/lib/wyatt_crud_bloc.dart | 21 ++ packages/wyatt_crud_bloc/pubspec.yaml | 27 +++ .../test/wyatt_crud_bloc_test.dart | 0 22 files changed, 1022 insertions(+) create mode 100644 packages/wyatt_crud_bloc/.gitignore create mode 100644 packages/wyatt_crud_bloc/CHANGELOG.md create mode 100644 packages/wyatt_crud_bloc/README.md create mode 100644 packages/wyatt_crud_bloc/analysis_options.yaml create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/builder/builder.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/builder/crud_builder.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/builder/crud_stream_builder.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/crud.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_cubit.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_state.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/models/model.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/models/models.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/models/queries/queries.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/models/queries/queries_firestore.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/models/queries/queries_interface.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firebase_database.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firestore.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_interface.dart create mode 100644 packages/wyatt_crud_bloc/lib/src/repositories/repositories.dart create mode 100644 packages/wyatt_crud_bloc/lib/wyatt_crud_bloc.dart create mode 100644 packages/wyatt_crud_bloc/pubspec.yaml create mode 100644 packages/wyatt_crud_bloc/test/wyatt_crud_bloc_test.dart diff --git a/packages/wyatt_crud_bloc/.gitignore b/packages/wyatt_crud_bloc/.gitignore new file mode 100644 index 00000000..65c34dc8 --- /dev/null +++ b/packages/wyatt_crud_bloc/.gitignore @@ -0,0 +1,10 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build outputs. +build/ + +# Omit committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/wyatt_crud_bloc/CHANGELOG.md b/packages/wyatt_crud_bloc/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/packages/wyatt_crud_bloc/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/packages/wyatt_crud_bloc/README.md b/packages/wyatt_crud_bloc/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/packages/wyatt_crud_bloc/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/packages/wyatt_crud_bloc/analysis_options.yaml b/packages/wyatt_crud_bloc/analysis_options.yaml new file mode 100644 index 00000000..8c9daa4e --- /dev/null +++ b/packages/wyatt_crud_bloc/analysis_options.yaml @@ -0,0 +1 @@ +include: package:wyatt_analysis/analysis_options.flutter.yaml diff --git a/packages/wyatt_crud_bloc/lib/src/crud/builder/builder.dart b/packages/wyatt_crud_bloc/lib/src/crud/builder/builder.dart new file mode 100644 index 00000000..032fa805 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/builder/builder.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export 'crud_builder.dart'; +export 'crud_stream_builder.dart'; diff --git a/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_builder.dart b/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_builder.dart new file mode 100644 index 00000000..d2c227bf --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_builder.dart @@ -0,0 +1,53 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wyatt_crud_bloc/src/crud/cubit/crud_cubit.dart'; +import 'package:wyatt_crud_bloc/src/models/model.dart'; + +class CrudBuilder> extends StatelessWidget { + const CrudBuilder({ + Key? key, + this.onIdle, + required this.onLoading, + required this.onError, + required this.onSuccess, + }) : super(key: key); + + final Widget Function(BuildContext, CrudState)? onIdle; + final Widget Function(BuildContext, CrudState) onLoading; + final Widget Function(BuildContext, CrudState) onError; + final Widget Function(BuildContext, CrudState) onSuccess; + + @override + Widget build(BuildContext context) { + return BlocBuilder, CrudState>( + builder: (context, state) { + switch (state.status) { + case CrudStatus.idle: + return onIdle?.call(context, state) ?? onError.call(context, state); + case CrudStatus.loading: + return onLoading.call(context, state); + case CrudStatus.failure: + return onError.call(context, state); + case CrudStatus.success: + return onSuccess.call(context, state); + } + }, + ); + } +} diff --git a/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_stream_builder.dart b/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_stream_builder.dart new file mode 100644 index 00000000..743b63e7 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/builder/crud_stream_builder.dart @@ -0,0 +1,55 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wyatt_crud_bloc/src/crud/cubit/crud_cubit.dart'; +import 'package:wyatt_crud_bloc/src/models/model.dart'; + +class CrudStreamBuilder> extends StatelessWidget { + const CrudStreamBuilder({ + Key? key, + required this.onLoading, + required this.onError, + required this.onStream, + }) : super(key: key); + + final Widget Function(BuildContext, List) onStream; + final Widget Function(BuildContext, CrudState) onLoading; + final Widget Function(BuildContext, CrudState) onError; + + @override + Widget build(BuildContext context) { + return BlocBuilder, CrudState>( + builder: (context, state) { + if (state.stream != null) { + return StreamBuilder>( + stream: state.stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + return onStream(context, snapshot.data!); + } else { + return onLoading(context, state); + } + }, + ); + } else { + return onError(context, state); + } + }, + ); + } +} diff --git a/packages/wyatt_crud_bloc/lib/src/crud/crud.dart b/packages/wyatt_crud_bloc/lib/src/crud/crud.dart new file mode 100644 index 00000000..cb18407c --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/crud.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export 'builder/builder.dart'; +export 'cubit/crud_cubit.dart'; diff --git a/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_cubit.dart b/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_cubit.dart new file mode 100644 index 00000000..6a73d408 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_cubit.dart @@ -0,0 +1,219 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:wyatt_crud_bloc/src/models/model.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart'; +import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart'; + +part 'crud_state.dart'; + +class CrudCubit> extends Cubit> { + final CrudRepositoryInterface _crudRepository; + + // ignore: prefer_const_constructors + CrudCubit(this._crudRepository) : super(CrudState()); + // Here we can't use `const` because we need the generic type T + + void reset() { + // ignore: prefer_const_constructors + emit(CrudState()); + // Same here, because of `const` we can't use T generic type + } + + Future create(Model object, {String? id}) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + await _crudRepository.create(object, id: id); + final data = state.data..addAll([object].cast()); + emit( + state.copyWith( + data: data, + status: CrudStatus.success, + ), + ); + } catch (e) { + // TODO(hpcl): implement Exception + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future delete(String id) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + await _crudRepository.delete(id); + final data = state.data + ..removeWhere((element) => element!.id != null && element.id == id); + emit( + state.copyWith( + data: data, + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future deleteAll() async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + await _crudRepository.deleteAll(); + emit( + state.copyWith( + data: [], + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future get(String id) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + final data = await _crudRepository.get(id); + emit( + state.copyWith( + data: [data].cast(), + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future getAll() async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + final data = await _crudRepository.getAll(); + emit( + state.copyWith( + data: data.cast(), + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future query(List conditions) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + final data = await _crudRepository.query(conditions); + emit( + state.copyWith( + data: data.cast(), + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future streamOf({String? id}) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + final Stream> data = _crudRepository.stream(id: id); + emit( + state.copyWith( + stream: data, + status: CrudStatus.success, + ), + ); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future update( + String id, { + Model? object, + Map? raw, + }) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + await _crudRepository.update( + id, + object: object, + raw: raw, + ); + emit(state.copyWith(status: CrudStatus.success)); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } + + Future updateAll(Map raw) async { + emit(state.copyWith(status: CrudStatus.loading)); + try { + await _crudRepository.updateAll(raw); + emit(state.copyWith(status: CrudStatus.success)); + } catch (e) { + emit( + state.copyWith( + status: CrudStatus.failure, + errorMessage: e.toString(), + ), + ); + } + } +} diff --git a/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_state.dart b/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_state.dart new file mode 100644 index 00000000..cd724eb4 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/crud/cubit/crud_state.dart @@ -0,0 +1,60 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +part of 'crud_cubit.dart'; + +enum CrudStatus { + idle, + loading, + success, + failure, +} + +class CrudState extends Equatable { + final CrudStatus status; + final List data; + final Stream>? stream; + final String? errorMessage; + + const CrudState({ + this.status = CrudStatus.idle, + this.data = const [], + this.stream, + this.errorMessage, + }); + + @override + List get props => [status, data]; + + CrudState copyWith({ + CrudStatus? status, + List? data, + Stream>? stream, + String? errorMessage, + }) { + return CrudState( + status: status ?? this.status, + data: data ?? this.data, + stream: stream ?? this.stream, + errorMessage: errorMessage ?? this.errorMessage, + ); + } + + @override + String toString() => + // ignore: lines_longer_than_80_chars + 'CrudState(status: $status, data: $data, stream: ${stream != null ? 'listening' : 'null'}, errorMessage: $errorMessage)'; +} diff --git a/packages/wyatt_crud_bloc/lib/src/models/model.dart b/packages/wyatt_crud_bloc/lib/src/models/model.dart new file mode 100644 index 00000000..fc590712 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/models/model.dart @@ -0,0 +1,21 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +abstract class Model { + String? get id; + Map toMap(); + T? from(O? object); +} diff --git a/packages/wyatt_crud_bloc/lib/src/models/models.dart b/packages/wyatt_crud_bloc/lib/src/models/models.dart new file mode 100644 index 00000000..4cff0335 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/models/models.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export 'model.dart'; +export 'queries/queries.dart'; diff --git a/packages/wyatt_crud_bloc/lib/src/models/queries/queries.dart b/packages/wyatt_crud_bloc/lib/src/models/queries/queries.dart new file mode 100644 index 00000000..7b786e30 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/models/queries/queries.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export 'queries_firestore.dart'; +export 'queries_interface.dart'; diff --git a/packages/wyatt_crud_bloc/lib/src/models/queries/queries_firestore.dart b/packages/wyatt_crud_bloc/lib/src/models/queries/queries_firestore.dart new file mode 100644 index 00000000..5b8e73ba --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/models/queries/queries_firestore.dart @@ -0,0 +1,88 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart'; + +class QueryParserFirestore implements QueryParserInterface { + @override + Query parser(QueryInterface condition, Object query) { + query as Query; + if (condition is WhereQueryInterface) { + switch (condition.type) { + case WhereQueryType.isEqualTo: + return query.where(condition.field, isEqualTo: condition.value); + case WhereQueryType.isNotEqualTo: + return query.where(condition.field, isNotEqualTo: condition.value); + case WhereQueryType.isLessThan: + return query.where(condition.field, isLessThan: condition.value); + case WhereQueryType.isLessThanOrEqualTo: + return query.where( + condition.field, + isLessThanOrEqualTo: condition.value, + ); + case WhereQueryType.isGreaterThan: + return query.where(condition.field, isGreaterThan: condition.value); + case WhereQueryType.isGreaterThanOrEqualTo: + return query.where( + condition.field, + isGreaterThanOrEqualTo: condition.value, + ); + case WhereQueryType.arrayContains: + return query.where(condition.field, arrayContains: condition.value); + case WhereQueryType.arrayContainsAny: + return query.where( + condition.field, + arrayContainsAny: condition.value as List, + ); + case WhereQueryType.whereIn: + return query.where( + condition.field, + whereIn: condition.value as List, + ); + case WhereQueryType.whereNotIn: + return query.where( + condition.field, + whereNotIn: condition.value as List, + ); + case WhereQueryType.isNull: + return query.where(condition.field, isNull: condition.value as bool); + } + } else if (condition is LimitQueryInterface) { + return query.limit(condition.limit); + } else if (condition is OrderByQueryInterface) { + return query.orderBy( + condition.field, + descending: !condition.ascending, + ); + } + return query; + } +} + +class WhereQueryFirestore extends WhereQueryInterface { + WhereQueryFirestore(WhereQueryType type, String field, Object value) + : super(type, field, value); +} + +class LimitQueryFirestore extends LimitQueryInterface { + LimitQueryFirestore(int limit) : super(limit); +} + +class OrderByQueryFirestore extends OrderByQueryInterface { + OrderByQueryFirestore(String field, {bool ascending = true}) + : super(field, ascending: ascending); +} diff --git a/packages/wyatt_crud_bloc/lib/src/models/queries/queries_interface.dart b/packages/wyatt_crud_bloc/lib/src/models/queries/queries_interface.dart new file mode 100644 index 00000000..01e8ea15 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/models/queries/queries_interface.dart @@ -0,0 +1,57 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ignore: one_member_abstracts +abstract class QueryParserInterface { + Object parser(QueryInterface condition, Object query); +} + +abstract class QueryInterface {} + +enum WhereQueryType { + isEqualTo, + isNotEqualTo, + isLessThan, + isLessThanOrEqualTo, + isGreaterThan, + isGreaterThanOrEqualTo, + arrayContains, + arrayContainsAny, + whereIn, + whereNotIn, + isNull, +} + +abstract class WhereQueryInterface extends QueryInterface { + final WhereQueryType type; + final String field; + final Object value; + + WhereQueryInterface(this.type, this.field, this.value); +} + +abstract class LimitQueryInterface extends QueryInterface { + final int limit; + + LimitQueryInterface(this.limit); +} + +abstract class OrderByQueryInterface extends QueryInterface { + final String field; + final bool ascending; + + OrderByQueryInterface(this.field, {this.ascending = true}); +} diff --git a/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firebase_database.dart b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firebase_database.dart new file mode 100644 index 00000000..9c095c85 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firebase_database.dart @@ -0,0 +1,102 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:firebase_database/firebase_database.dart'; +import 'package:wyatt_crud_bloc/src/models/model.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart'; +import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart'; + +class CrudRepositoryFirebaseDatabase implements CrudRepositoryInterface { + final FirebaseDatabase _firebaseDatabase; + final Model _parser; + + late DatabaseReference _rootReference; + + CrudRepositoryFirebaseDatabase( + String root, + Model parser, { + FirebaseDatabase? firebaseDatabase, + }) : _firebaseDatabase = firebaseDatabase ?? FirebaseDatabase.instance, + _parser = parser { + _rootReference = _firebaseDatabase.ref(root); + } + + @override + Future create(Model object, {String? id}) { + DatabaseReference _reference = _rootReference; + if (id != null) { + _reference = _reference.child(id); + } + return _reference.set(object.toMap()); + } + + @override + Future delete(String id) { + final DatabaseReference _reference = _rootReference.child(id); + return _reference.remove(); + } + + @override + Future deleteAll() { + return _rootReference.remove(); + } + + @override + Future get(String id) async { + final DatabaseEvent _event = await _rootReference.child(id).once(); + return _parser.from(_event.snapshot.value); + } + + @override + Future> getAll() async { + final DatabaseEvent _event = await _rootReference.once(); + final List _objects = []; + _event.snapshot.children.map((e) => _objects.add(_parser.from(e.value))); + return _objects; + } + + @override + Future> query(List conditions) { + // TODO(hpcl): implement query + throw UnimplementedError(); + } + + @override + Stream> stream({String? id, List? conditions}) { + DatabaseReference _reference = _rootReference; + if (id != null) { + _reference = _reference.child(id); + } + + return _reference.onValue.map((e) { + final List _objects = []; + e.snapshot.children.map((e) => _objects.add(_parser.from(e.value))); + return _objects; + }); + } + + @override + Future update(String id, {Model? object, Map? raw}) { + // TODO(hpcl): implement update + throw UnimplementedError(); + } + + @override + Future updateAll(Map raw) { + // TODO(hpcl): implement updateAll + throw UnimplementedError(); + } +} diff --git a/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firestore.dart b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firestore.dart new file mode 100644 index 00000000..7b49bb9b --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_firestore.dart @@ -0,0 +1,146 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:wyatt_crud_bloc/src/models/model.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_firestore.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart'; +import 'package:wyatt_crud_bloc/src/repositories/crud_repository_interface.dart'; + +class CrudRepositoryFirestore implements CrudRepositoryInterface { + final FirebaseFirestore _firestore; + final Model _parser; + + late CollectionReference _collectionReference; + + CrudRepositoryFirestore( + String collection, + Model parser, { + FirebaseFirestore? firestore, + }) : _firestore = firestore ?? FirebaseFirestore.instance, + _parser = parser { + _collectionReference = _firestore.collection(collection); + } + + @override + Future create(Model object, {String? id}) { + if (id != null) { + return _collectionReference.doc(id).set(object.toMap()); + } else { + return _collectionReference.add(object.toMap()); + } + } + + @override + Future delete(String id) { + return _collectionReference.doc(id).delete(); + } + + @override + Future deleteAll() async { + final _batch = _firestore.batch(); + final QuerySnapshot snapshots = await _collectionReference.get(); + for (final DocumentSnapshot snapshot in snapshots.docs) { + _batch.delete(snapshot.reference); + } + return _batch.commit(); + } + + @override + Future get(String id) async { + final DocumentSnapshot snapshot = await _collectionReference.doc(id).get(); + return _parser.from(snapshot); + } + + @override + Future> getAll() async { + final QuerySnapshot snapshots = await _collectionReference.get(); + return snapshots.docs.map(_parser.from).toList(); + } + + @override + Future> query(List conditions) async { + Query query = _collectionReference; + for (final condition in conditions) { + query = QueryParserFirestore().parser(condition, query); + } + final QuerySnapshot snapshots = await query.get(); + return snapshots.docs.map(_parser.from).toList(); + } + + @override + Stream> stream({ + String? id, + List? conditions, + bool includeMetadataChanges = false, + }) { + if (id != null) { + return _collectionReference + .doc(id) + .snapshots( + includeMetadataChanges: includeMetadataChanges, + ) + .map>( + (DocumentSnapshot snapshot) => [_parser.from(snapshot)], + ); + } else { + if (conditions != null) { + Query query = _collectionReference; + for (final condition in conditions) { + query = QueryParserFirestore().parser(condition, query); + } + return query + .snapshots( + includeMetadataChanges: includeMetadataChanges, + ) + .map((querySnapshot) { + return querySnapshot.docs.map(_parser.from).toList(); + }); + } else { + return _collectionReference + .snapshots( + includeMetadataChanges: includeMetadataChanges, + ) + .map((querySnapshot) { + return querySnapshot.docs.map(_parser.from).toList(); + }); + } + } + } + + @override + Future update(String id, {Model? object, Map? raw}) { + if (object != null) { + return _collectionReference.doc(id).update(object.toMap()); + } else { + if (raw != null) { + return _collectionReference.doc(id).update(raw); + } else { + throw Exception('You must provide an object or a raw map'); + } + } + } + + @override + Future updateAll(Map raw) async { + final _batch = _firestore.batch(); + final QuerySnapshot snapshots = await _collectionReference.get(); + for (final DocumentSnapshot snapshot in snapshots.docs) { + _batch.update(snapshot.reference, raw); + } + return _batch.commit(); + } +} diff --git a/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_interface.dart b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_interface.dart new file mode 100644 index 00000000..15eb3054 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/repositories/crud_repository_interface.dart @@ -0,0 +1,30 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:wyatt_crud_bloc/src/models/model.dart'; +import 'package:wyatt_crud_bloc/src/models/queries/queries_interface.dart'; + +abstract class CrudRepositoryInterface { + Future create(Model object, {String? id}); + Future delete(String id); + Future deleteAll(); + Future get(String id); + Future> getAll(); + Future> query(List conditions); + Stream> stream({String? id, List? conditions}); + Future update(String id, {Model? object, Map? raw}); + Future updateAll(Map raw); +} diff --git a/packages/wyatt_crud_bloc/lib/src/repositories/repositories.dart b/packages/wyatt_crud_bloc/lib/src/repositories/repositories.dart new file mode 100644 index 00000000..aa0511e6 --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/src/repositories/repositories.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +export 'crud_repository_firestore.dart'; +export 'crud_repository_interface.dart'; diff --git a/packages/wyatt_crud_bloc/lib/wyatt_crud_bloc.dart b/packages/wyatt_crud_bloc/lib/wyatt_crud_bloc.dart new file mode 100644 index 00000000..fffec70f --- /dev/null +++ b/packages/wyatt_crud_bloc/lib/wyatt_crud_bloc.dart @@ -0,0 +1,21 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +library wyatt_crud_bloc; + +export 'src/crud/crud.dart'; +export 'src/models/models.dart'; +export 'src/repositories/repositories.dart'; diff --git a/packages/wyatt_crud_bloc/pubspec.yaml b/packages/wyatt_crud_bloc/pubspec.yaml new file mode 100644 index 00000000..97429796 --- /dev/null +++ b/packages/wyatt_crud_bloc/pubspec.yaml @@ -0,0 +1,27 @@ +name: wyatt_crud_bloc +description: Create/Read/Update/Delete BLoC for Flutter +repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_crud_bloc +version: 0.0.2 + +environment: + sdk: '>=2.16.2 <3.0.0' + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_bloc: ^8.0.1 + equatable: ^2.0.3 + cloud_firestore: ^3.1.12 + firebase_database: ^9.0.11 + +dev_dependencies: + flutter_test: + sdk: flutter + bloc_test: ^9.0.3 + wyatt_analysis: + git: + url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages + ref: wyatt_analysis-v2.0.0 + path: packages/wyatt_analysis diff --git a/packages/wyatt_crud_bloc/test/wyatt_crud_bloc_test.dart b/packages/wyatt_crud_bloc/test/wyatt_crud_bloc_test.dart new file mode 100644 index 00000000..e69de29b