Skip to content

Commit

Permalink
table schema
Browse files Browse the repository at this point in the history
  • Loading branch information
a-givertzman committed Nov 27, 2023
1 parent 94496bf commit 01f8d62
Show file tree
Hide file tree
Showing 6 changed files with 401 additions and 0 deletions.
27 changes: 27 additions & 0 deletions lib/src/sql/sql.dart
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;
}
}
40 changes: 40 additions & 0 deletions lib/src/table_schema/field.dart
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;
}
54 changes: 54 additions & 0 deletions lib/src/table_schema/field_value.dart
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,
}
34 changes: 34 additions & 0 deletions lib/src/table_schema/relation.dart
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;
}
221 changes: 221 additions & 0 deletions lib/src/table_schema/schema.dart
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");
}
}
}
}
}
Loading

0 comments on commit 01f8d62

Please sign in to comment.