-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
94496bf
commit 01f8d62
Showing
6 changed files
with
401 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import 'package:dart_api_client/src/table_schema/schema_entry.dart'; | ||
|
||
/// | ||
/// - hosds simple SQL | ||
/// - alows to build sql with multiple values | ||
class Sql { | ||
/// | ||
final String _sql; | ||
List<SchemaEntry> _enties; | ||
/// | ||
Sql({ | ||
required String sql, | ||
List<SchemaEntry> values = const [], | ||
}) : | ||
_sql = sql, | ||
_enties = values; | ||
/// | ||
/// adding values to the sql | ||
void addValues(SchemaEntry entry) { | ||
_enties.add(entry); | ||
} | ||
/// | ||
/// | ||
String build() { | ||
return _sql; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
|
||
import 'package:dart_api_client/src/table_schema/relation.dart'; | ||
|
||
/// | ||
/// Replresentation settings for table column | ||
class Field { | ||
final String _key; | ||
final String _name; | ||
final bool _hidden; | ||
final bool _edit; | ||
final Relation _relation; | ||
/// | ||
const Field({ | ||
required String key, | ||
String? name, | ||
bool hidden = false, | ||
bool edit = true, | ||
Relation? relation, | ||
}) : | ||
_key = key, | ||
_name = name ?? key, | ||
_hidden = hidden, | ||
_edit = edit, | ||
_relation = relation ?? const Relation.empty(); | ||
/// | ||
/// | ||
String get key => _key; | ||
/// | ||
/// | ||
String get name => _name; | ||
/// | ||
/// | ||
bool get hidden => _hidden; | ||
/// | ||
/// | ||
bool get edit => _edit; | ||
/// | ||
/// | ||
Relation get relation => _relation; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
class FieldValue<T> { | ||
T _value; | ||
final FieldType _type; | ||
/// | ||
/// | ||
FieldValue( | ||
T value, { | ||
FieldType type = FieldType.string, | ||
}) : | ||
_value = value, | ||
_type = type; | ||
/// | ||
/// | ||
T get value => _value; | ||
/// | ||
/// | ||
String? get str { | ||
if (_value == null) { | ||
return null; | ||
} | ||
switch (type) { | ||
case FieldType.bool: | ||
return '$_value'; | ||
case FieldType.int: | ||
return '$_value'; | ||
case FieldType.double: | ||
return '$_value'; | ||
case FieldType.string: | ||
return "'$_value'"; | ||
// default: | ||
} | ||
} | ||
|
||
/// | ||
/// | ||
FieldType get type => _type; | ||
/// | ||
/// Returns true if changed | ||
bool update(T value) { | ||
if (_value != value) { | ||
_value = value; | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
|
||
|
||
enum FieldType { | ||
bool, | ||
int, | ||
double, | ||
string, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/// | ||
/// - [id] - where from get the data | ||
/// - [field] - name of field to be presented from data | ||
class Relation { | ||
final String _id; | ||
final String _field; | ||
final bool _isEmpty; | ||
/// | ||
/// | ||
const Relation({ | ||
required String id, | ||
required String field, | ||
}) : | ||
_id = id, | ||
_field = field, | ||
_isEmpty = false; | ||
/// | ||
const Relation.empty() : | ||
_id = '', | ||
_field = '', | ||
_isEmpty = true; | ||
/// | ||
/// | ||
String get id => _id; | ||
/// | ||
/// | ||
String get field => _field; | ||
/// | ||
/// | ||
bool get isEmpty => _isEmpty; | ||
/// | ||
/// | ||
bool get isNotEmpty => !_isEmpty; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
import 'package:dart_api_client/dart_api_client.dart'; | ||
import 'package:dart_api_client/src/sql/sql.dart'; | ||
import 'package:dart_api_client/src/table_schema/field.dart'; | ||
import 'package:dart_api_client/src/table_schema/schema_entry.dart'; | ||
import 'package:hmi_core/hmi_core_failure.dart'; | ||
import 'package:hmi_core/hmi_core_log.dart'; | ||
import 'package:hmi_core/hmi_core_result_new.dart'; | ||
|
||
typedef SqlBuilder<T extends SchemaEntry> = Sql Function(Sql sql, T entry); | ||
|
||
|
||
/// | ||
/// A collection of the SchameEntry, | ||
/// abstruction on the SQL table rows | ||
class Schema<T extends SchemaEntry> { | ||
late final Log _log; | ||
final ApiAddress _address; | ||
final String _authToken; | ||
final String _database; | ||
final bool _keepAlive; | ||
final bool _debug; | ||
final List<Field> _fields; | ||
final Map<String, T> _entries = {}; | ||
final Sql Function(List<dynamic>? values) _fetchSqlBuilder; | ||
final SqlBuilder<T>? _insertSqlBuilder; | ||
final SqlBuilder<T>? _updateSqlBuilder; | ||
final Map<String, Schema> _relations; | ||
final Map<T, Function> _entryFromFactories; | ||
final Map<T, Function> _entryEmptyFactories; | ||
Sql _sql = Sql(sql: ''); | ||
/// | ||
/// A collection of the SchameEntry, | ||
/// abstruction on the SQL table rows | ||
/// - [keys] - list of table field names | ||
Schema({ | ||
required ApiAddress address, | ||
required String authToken, | ||
required String database, | ||
required List<Field> fields, | ||
bool keepAlive = false, | ||
bool debug = false, | ||
required Sql Function(List<dynamic>? values) fetchSqlBuilder, | ||
SqlBuilder<T>? insertSqlBuilder, | ||
SqlBuilder<T>? updateSqlBuilder, | ||
Map<String, Schema> relations = const {}, | ||
required Map<T, Function> entryFromFactories, | ||
required Map<T, Function> entryEmptyFactories, | ||
}) : | ||
_address = address, | ||
_authToken = authToken, | ||
_database = database, | ||
_fields = fields, | ||
_keepAlive = keepAlive, | ||
_debug = debug, | ||
_fetchSqlBuilder = fetchSqlBuilder, | ||
_insertSqlBuilder = insertSqlBuilder, | ||
_updateSqlBuilder = updateSqlBuilder, | ||
_relations = relations, | ||
_entryFromFactories = entryFromFactories, | ||
_entryEmptyFactories = entryEmptyFactories { | ||
_log = Log("$runtimeType"); | ||
} | ||
/// | ||
/// Returns a list of table field names | ||
List<Field> get fields { | ||
return _fields; | ||
} | ||
/// | ||
/// Returns a list of table field keys | ||
List<String> get keys { | ||
return _fields.map((field) => field.key).toList(); | ||
} | ||
/// | ||
/// | ||
List<T> get entries => _entries.values.toList(); | ||
/// | ||
/// Fetchs data with existing sql | ||
Future<Result<List<SchemaEntry>, Failure>> refresh() { | ||
if (_sql.build().isEmpty) { | ||
_sql = _fetchSqlBuilder([]); | ||
} | ||
return fetchWith(_sql); | ||
} | ||
/// | ||
/// Fetchs data with new sql built from [values] calling fetchSqlBuilder(values) | ||
Future<Result<List<T>, Failure>> fetch(List values) async { | ||
await fetchRelations(); | ||
_sql = _fetchSqlBuilder(values); | ||
return fetchWith(_sql); | ||
} | ||
/// | ||
/// Returns relation Result<Scheme> if exists else Result<Failure> | ||
Result<Schema, Failure> relation(String id) { | ||
final rel = _relations[id]; | ||
if (rel != null) { | ||
return Ok(rel); | ||
} else { | ||
return Err(Failure( | ||
message: "$runtimeType.relation | id: $id - not found", | ||
stackTrace: StackTrace.current, | ||
)); | ||
} | ||
} | ||
/// | ||
T _makeEntry(Map<String, dynamic> row) { | ||
final constructor = _entryFromFactories[T]; | ||
if (constructor != null) { | ||
return constructor(row); | ||
} | ||
throw Failure( | ||
message: "$runtimeType._makeEntry | Can't find constructor for $T", | ||
stackTrace: StackTrace.current, | ||
); | ||
} | ||
/// | ||
/// Fetchs data with new [sql] | ||
Future<Result<List<T>, Failure>> fetchWith(Sql sql) { | ||
final request = ApiRequest( | ||
address: _address, | ||
query: SqlQuery( | ||
authToken: _authToken, | ||
database: _database, | ||
sql: sql.build(), | ||
keepAlive: _keepAlive, | ||
debug: _debug, | ||
), | ||
); | ||
return request.fetch().then((result) { | ||
return result.fold( | ||
onData: (replay) { | ||
if (replay.hasError) { | ||
return Err(Failure(message: replay.error.message, stackTrace: StackTrace.current)); | ||
} else { | ||
_entries.clear(); | ||
final rows = replay.data; | ||
for (final row in rows) { | ||
final entry = _makeEntry(row); | ||
if (_entries.containsKey(entry.key)) { | ||
throw Failure( | ||
message: "$runtimeType.fetchWith | dublicated entry key: ${entry.key}", | ||
stackTrace: StackTrace.current, | ||
); | ||
} | ||
_entries[entry.key] = entry; | ||
} | ||
} | ||
return Ok(_entries.values.toList()); | ||
}, | ||
onError: (err) { | ||
return Err(err); | ||
}, | ||
); | ||
}); | ||
} | ||
/// | ||
/// Inserts new entry into the table scheme | ||
Future<Result<void, Failure>> insert({T? entry}) { | ||
T entry_; | ||
if (entry != null) { | ||
entry_ = entry; | ||
} else { | ||
final constructor = _entryEmptyFactories[T]; | ||
if (constructor != null) { | ||
entry_ = constructor(); | ||
} else { | ||
throw Failure( | ||
message: "$runtimeType._makeEntry | Can't find constructor for $T", | ||
stackTrace: StackTrace.current, | ||
); | ||
} | ||
} | ||
final builder = _insertSqlBuilder; | ||
if (builder != null) { | ||
final initialSql = Sql(sql: ''); | ||
final sql = builder(initialSql, entry_); | ||
return fetchWith(sql).then((result) { | ||
if (result is Ok) { | ||
_entries[entry_.key] = entry_; | ||
} | ||
return result; | ||
}); | ||
} | ||
throw Failure( | ||
message: "$runtimeType.insert | insertSqlBuilder is not initialized", | ||
stackTrace: StackTrace.current, | ||
); | ||
} | ||
/// | ||
/// Updates entry of the table scheme | ||
Future<Result<void, Failure>> update(T entry) { | ||
final builder = _updateSqlBuilder; | ||
if (builder != null) { | ||
final initialSql = Sql(sql: ''); | ||
final sql = builder(initialSql, entry); | ||
return fetchWith(sql).then((result) { | ||
if (result is Ok) { | ||
_entries[entry.key] = entry; | ||
} | ||
return result; | ||
}); | ||
} | ||
throw Failure( | ||
message: "$runtimeType.update | updateSqlBuilder is not initialized", | ||
stackTrace: StackTrace.current, | ||
); | ||
} | ||
/// | ||
/// Fetchs data of the relation schemes only (with existing sql) | ||
Future<void> fetchRelations() async { | ||
for (final field in _fields) { | ||
if (field.relation.isNotEmpty) { | ||
switch (relation(field.relation.id)) { | ||
case Ok(:final value): | ||
await value.refresh(); | ||
case Err(:final error): | ||
_log.warning(".fetchRelations | relation '${field.relation}' - not found\n\terror: $error"); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.