--- /dev/null
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+
+import 'src/model/standard/role_model.dart';
+import 'src/model/standard/user_model.dart';
+
+void main(List<String> argv) async {
+ final logger = MemoryLogger(LEVEL_FINE);
+ final dbHelper = DbHelper(logger);
+ if (argv.isEmpty) {
+ argv = ['export-sql'];
+ }
+ final mode = argv[0];
+ argv.removeAt(0);
+ final options = <String>[];
+ argv = StringHelper.splitArgv(argv, options);
+ switch (mode) {
+ case 'export-sql':
+ dbHelper.exportSql(argv, options);
+ break;
+ default:
+ logger.error('unknown mode: $mode');
+ break;
+ }
+}
+
+class DbHelper {
+ final BaseLogger logger;
+
+ DbHelper(this.logger);
+
+ void exportSql(List<String> args, List<String> options) {
+ String directory;
+ String value;
+ for (var opt in options) {
+ value = StringUtils.stringOption('directory', 'd', opt);
+ if (value != null) {
+ directory = value;
+ }
+ }
+ directory ??= 'data';
+ final dirDDL = FileSync.joinPaths(directory, 'ddl');
+ final dirREST = FileSync.joinPaths(directory, 'rest');
+ FileSync.ensureDirectory(dirDDL);
+ FileSync.ensureDirectory(dirREST);
+ if (args.isEmpty) {
+ args = ['user', 'role'];
+ }
+ while (args.isNotEmpty) {
+ final name = args[0];
+ args.removeAt(0);
+ ModuleModel module;
+ switch (name) {
+ case 'user':
+ module = UserModel(logger);
+ break;
+ case 'role':
+ module = RoleModel(logger);
+ break;
+ default:
+ logger.error('unknown table');
+ break;
+ }
+ if (module != null) {
+ module.parse();
+ var filename = FileSync.joinPaths(dirDDL, '$name.sql');
+ FileSync.toFile(filename, module.exportSqlCreateTable());
+ print('exported: $filename');
+ filename = FileSync.joinPaths(dirREST, '$name.yaml');
+ FileSync.toFile(filename, module.exportSqlBackend());
+ print('exported: $filename');
+ }
+ }
+ }
+
+ void usage(String error) {
+ print('''usage: dbhelper <mode> [<args>]
+<mode>:
+ create-sql [<module>] [<opt>]
+ <module>: 'role', 'user' ...
+ <opt>:
+ -d<dir> or --directory=<dir>:
+ the base directory used for the exported files.
+Examples:
+dbhelper create-sql role role.sql -d/tmp
+dbhelper create-sql --directory=/opt/sql-data
+''');
+ }
+}
import 'package:flutter/material.dart';
import 'package:flutter_bones/app.dart';
-void main() {
+void main(List<String> args) {
+ //args.length > 1 && args[0].startsWith('--') &&
runApp(BoneApp());
}
-
import 'package:dart_bones/dart_bones.dart';
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:intl/intl.dart';
class StringHelper {
static final regExpTrue = RegExp(r'^(true|yes)$', caseSensitive: false);
static final regExpFalse = RegExp(r'^(false|no)$', caseSensitive: false);
+ static const locale = 'de_DE';
+
+ /// Converts a [dateTime] into a string.
+ /// If [dateTime] is null the current date and time is used.
+ /// If [sortable] is true the result is sortable (year.month.day ...).
+ /// [separator] is the part between date and time.
+ /// If [withSeconds] is true the result contains the seconds otherwise not.
+ static dateTimeToString(DateTime dateTime,
+ {String separator = ' ', withSeconds: true, sortable = false}) {
+ dateTime ??= DateTime.now();
+ String rc;
+ if (sortable) {
+ rc = DateFormat(withSeconds
+ ? 'yyyy.MM.dd${separator}HH:mm:ss'
+ : 'yyyy.MM.dd${separator}HH:mm')
+ .format(dateTime);
+ } else {
+ rc = dateToString(dateTime) +
+ separator +
+ (withSeconds
+ ? DateFormat.Hms().format(dateTime)
+ : DateFormat.Hm(locale).format(dateTime));
+ }
+ return rc;
+ }
+
+ /// Converts a [date] into a string.
+ /// If [date] is null the current date is used.
+ /// If [sortable] is true the result is sortable (year.month.day).
+ static dateToString(DateTime date, {sortable = false}) {
+ date ??= DateTime.now();
+ final formatter =
+ sortable ? DateFormat('yyyy.MM.dd') : DateFormat.yMd(locale);
+ return formatter.format(date);
+ }
/// Converts a string to a given [dataType].
static dynamic fromString(String value, DataType dataType) {
}
return rc;
}
+
+ /// Splits a argument list into an [option] list and a true arguments list.
+ /// [args]: the argument list to split.
+ /// [options]: OUT: the options list
+ /// Returns the true argument list (arguments without options).
+ static List<String> splitArgv(List<String> args, List<String> options) {
+ final rc = <String>[];
+ for (var arg in args) {
+ if (arg.startsWith('-')) {
+ options.add(arg);
+ } else {
+ rc.add(arg);
+ }
+ }
+ return rc;
+ }
}
import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter/cupertino.dart';
import 'model_base.dart';
import 'model_types.dart';
/// Describes a column of a database table.
class ColumnModel extends WidgetModel {
static final regExprOptions = RegExp(
- r'^(undef|readonly|disabled|hidden|null|notnull|primary|required|unique)$');
+ r'^(undef|readonly|disabled|doStore|hidden|null|notnull|password|primary|required|unique)$');
String name;
String label;
String toolTip;
final Map<String, dynamic> map;
String foreignKey;
+ /// A constructor used in the parser.
ColumnModel(this.table, this.map, BaseLogger logger)
: super(null, null, WidgetModelType.column, logger);
+ /// A constructor for columns created by the program.
+ ColumnModel.raw(
+ {@required this.name,
+ @required this.table,
+ @required this.label,
+ @required this.dataType,
+ @required this.options,
+ this.size,
+ this.map,
+ BaseLogger logger})
+ : super(null, null, WidgetModelType.column, logger);
+
/// Dumps the instance into a [StringBuffer]
StringBuffer dump(StringBuffer stringBuffer) {
stringBuffer.write(
/// Parses the map and stores the data in the instance.
void parse() {
- name = parseString('name', map, required: true);
+ name = parseString('column', map, required: true);
checkSuperfluousAttributes(
map,
- 'dataType foreignKey label name options rows size tooTip widgetType'
+ 'column dataType foreignKey label options rows size tooTip widgetType'
.split(' '));
dataType =
parseEnum<DataType>('dataType', map, DataType.values, required: true);
label = parseString('label', map, required: false);
toolTip = parseString('toolTip', map, required: false);
size = parseInt('size', map, required: dataType == DataType.string);
- rows = parseInt('size', map);
+ rows = parseInt('rows', map);
options = parseOptions('options', map);
checkOptionsByRegExpr(options, regExprOptions);
+ if (options.contains('primary')) {
+ if (!options.contains('notnull')) {
+ options.add('notnull');
+ }
+ if (!options.contains('unique')) {
+ options.add('unique');
+ }
+ }
}
@override
DataType dataType;
FormFieldSetter onSaved;
List<String> options;
+ FilterType filterType;
final Map<String, dynamic> map;
var _value;
name = parseString('name', map, required: true);
label = parseString('label', map, required: false);
toolTip = parseString('toolTip', map, required: false);
+ filterType = parseEnum<FilterType>('filterType', map, FilterType.values);
dataType = parseEnum<DataType>('dataType', map, DataType.values);
if (dataType == null) {
if (!map.containsKey(key)) {
if (required) {
logger.error('missing int attribute "$key" in ${fullName()}');
- } else {
- final value = map[key];
- if (value != null) {
- if (value.runtimeType == int) {
- rc = map[key];
- } else if (value.runtimeType == String) {
- if (Validation.isInt(value)) {
- rc = StringUtils.asInt(value);
- } else {
- logger.error('not an integer: $value map[$key] in {fullName()}');
- }
+ }
+ } else {
+ final value = map[key];
+ if (value != null) {
+ if (value.runtimeType == int) {
+ rc = map[key];
+ } else if (value.runtimeType == String) {
+ if (Validation.isInt(value)) {
+ rc = StringUtils.asInt(value);
} else {
logger.error('not an integer: $value map[$key] in {fullName()}');
}
+ } else {
+ logger.error('not an integer: $value map[$key] in {fullName()}');
}
}
}
}
/// Fetches an entry from a map addressed by a [key].
- /// This entry is splitted by the delimiter given at index 0.
+ /// This entry is split by the delimiter given at index 0.
/// Example: ";a;b" returns ['a', 'b'].
/// An error is logged if [required] is true and the map does not contain the key.
List<dynamic> parseValueList(
if (ModelBase.isList(map[key])) {
rc = map[key];
} else {
- final strings = parseStringList(key, map, required: required);
- rc = strings.map((item) {
- var rc2;
- switch (dataType) {
- case DataType.int:
- rc2 = StringUtils.asInt(item);
- break;
- default:
- logger.error('unknown dataType in parseValueList()');
- rc2 = item;
- }
- return rc2;
- });
+ final strings = parseStringList(key, map, required: required).toList();
+ if (strings.isNotEmpty) {
+ rc = strings.map((item) {
+ var rc2;
+ switch (dataType) {
+ case DataType.int:
+ rc2 = StringUtils.asInt(item);
+ break;
+ default:
+ logger.error('unknown dataType in parseValueList()');
+ rc2 = item;
+ }
+ return rc2;
+ });
+ }
}
return rc;
}
dateTil,
dateTimeFrom,
dateTimeTil,
+ equals,
pattern,
}
ModuleModel(this.map, BaseLogger logger) : super(logger);
+ /// Adds the column [name] from [source] to [target] if that column is not member of [target].
+ void addColumnIfMissing(
+ List<ColumnModel> target, List<ColumnModel> source, String name) {
+ final isEmpty = target.isEmpty;
+ final first = isEmpty
+ ? null
+ : target.firstWhere((col) => col.name == name, orElse: () => null);
+ final first2 = source.isEmpty
+ ? null
+ : source.firstWhere((col) => col.name == name, orElse: () => null);
+ if (first == null && first2 != null) {
+ target.add(first2);
+ }
+ }
+
/// Appends a [page] to the instance.
void addPage(PageModel page) {
if (pages.where((element) => element.name == page.name).isNotEmpty) {
return stringBuffer;
}
+ /// Writes the insert SQL part into the [buffer].
+ void exportInsert(StringBuffer buffer, TableModel table) {
+ buffer.write(''' sql: "INSERT INTO ${table.name}(''');
+ final columns = table.columns
+ .where((col) =>
+ col.hasOption('doStore') ||
+ (!col.hasOption('primary') && (!col.hasOption('hidden'))))
+ .toList();
+ addColumnIfMissing(columns, table.columns, '${table.name}_createdat');
+ addColumnIfMissing(columns, table.columns, '${table.name}_createdby');
+ buffer.write(columns.fold(
+ '',
+ (prev, col) =>
+ (prev as String) +
+ ((prev as String).isEmpty ? col.name : ',' + col.name)));
+ buffer.write(')\n');
+ buffer.write(' VALUES(');
+ buffer.write(columns.fold(
+ '',
+ (prev, col) =>
+ (prev as String) +
+ ((prev as String).isEmpty ? '' : ',') +
+ (col.name.endsWith('createdat') ? 'NOW()' : (':' + col.name))));
+ buffer.write(');"\n');
+ }
+
+ /// Writes the list SQL part into the [buffer].
+ void exportList(StringBuffer buffer, TableModel table) {
+ final page = pages
+ .firstWhere((element) => element.pageModelType == PageModelType.list,
+ orElse: () => null);
+ if (page != null) {
+ buffer.write(''' - name: list
+ type: list
+ sql: "SELECT * from ${table.name}\n''');
+ // @ToDo: joins
+ final fields = page.fields.values
+ .where((item) => item is FieldModel && item.filterType != null);
+ if (fields.isEmpty) {
+ buffer.write(' WHERE 1;"');
+ } else {
+ buffer.write(' WHERE ');
+ var first = true;
+ for (var field in fields) {
+ if (!first) {
+ buffer.write(' AND ');
+ }
+ switch (field.filterType) {
+ case FilterType.dateFrom:
+ case FilterType.dateTimeFrom:
+ buffer.write('${field.name}>=:${field.name}');
+ break;
+ case FilterType.dateTil:
+ case FilterType.dateTimeTil:
+ buffer.write('${field.name}<=:${field.name}');
+ break;
+ case FilterType.pattern:
+ buffer.write('${field.name} like :${field.name}');
+ break;
+ }
+ first = false;
+ }
+ buffer.write(';"\n');
+ }
+ }
+ }
+
+ /// Exports a YAML file (as String) describing the SQL statements for insert, update ...
+ /// testDate: the fix date 2020.01.01 will be used (for unit tests)
+ String exportSqlBackend({bool testDate: false}) {
+ StringBuffer buffer = StringBuffer();
+ final date = StringHelper.dateTimeToString(
+ testDate ? DateTime(2020, 1, 1) : null,
+ sortable: true);
+ final table = mainTable();
+ if (table != null) {
+ buffer.write('''---
+# configuration of the bones backend for ${table.name}:
+created: $date
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+ - module: ${table.name}
+ list:
+ - name: insert
+ type: insert
+''');
+ exportInsert(buffer, table);
+ buffer.write(''' - name: update
+ type: update
+''');
+ exportUpdate(buffer, table);
+ buffer.write(''' - name: delete
+ type: delete
+ sql: "DELETE from ${table.name} WHERE ${table.name}_id=:${table.name}_id;"
+ - name: record
+ type: record
+ sql: "SELECT * from ${table.name} WHERE ${table.name}_id=:${table.name}_id;"
+''');
+ exportList(buffer, table);
+ }
+ return buffer.toString();
+ }
+
/// Exports the SQL statement to create the module tables.
- String exportSql() {
+ String exportSqlCreateTable() {
StringBuffer buffer = StringBuffer();
for (var table in tables) {
- buffer.write('drop table if exists ${table.name};\n');
- buffer.write('create table ${table.name} (\n');
+ buffer.write('DROP TABLE IF EXISTS ${table.name};\n');
+ buffer.write('CREATE TABLE ${table.name} (\n');
for (var column in table.columns) {
String type;
switch (column.dataType) {
case DataType.bool:
- type = 'char(1)';
+ type = 'CHAR(1)';
break;
case DataType.currency:
- type = 'int(10)';
+ type = 'INT(10)';
break;
case DataType.date:
- type = 'date';
+ type = 'DATE';
break;
case DataType.dateTime:
- type = 'timestamp';
+ type = 'TIMESTAMP';
break;
case DataType.float:
- type = 'double';
+ type = 'DOUBLE';
break;
case DataType.int:
- type = 'int(10)';
+ type = 'INT(10)';
break;
case DataType.reference:
- type = 'int(10)';
+ type = 'INT(10) UNSIGNED';
break;
case DataType.string:
if (column.size == null) {
- type = 'varchar(255)';
+ type = 'VARCHAR(255)';
} else if (column.size <= 255) {
- type = 'varchar($column.size})';
+ type = 'VARCHAR(${column.size})';
} else if (column.size <= 0xffff) {
- type = 'text';
+ type = 'TEXT';
} else {
- type = 'mediumtext';
+ type = 'MEDIUMTEXT';
}
break;
}
String options = '';
for (var option in column.options) {
- if ('notnull null primary unique'.contains(option)) {
+ if ('notnull null unique'.contains(option)) {
options += ' ' + option;
}
}
- buffer.write(' ${column.name} $type$options;\n');
+ buffer.write(' ${column.name} $type$options,\n');
}
+ buffer.write(' PRIMARY KEY(${table.name}_id)\n');
buffer.write(');\n');
}
final rc = buffer.toString();
return rc;
}
+ /// Writes the insert SQL part into the [buffer].
+ void exportUpdate(StringBuffer buffer, TableModel table) {
+ buffer.write(' sql: "UPDATE ${table.name} SET\n ');
+ final columns = table.columns
+ .where((col) =>
+ col.hasOption('doStore') ||
+ (!col.hasOption('primary') && !col.hasOption('hidden')))
+ .toList();
+ addColumnIfMissing(columns, table.columns, '${table.name}_changedat');
+ addColumnIfMissing(columns, table.columns, '${table.name}_changedby');
+ buffer.write(columns.fold(
+ '',
+ (prev, col) =>
+ (prev as String) +
+ ((prev as String).isEmpty ? '' : ',') +
+ col.name +
+ '=' +
+ ((col.name.endsWith('changedat') ? 'NOW()' : ':' + col.name))));
+ buffer.write('\n WHERE ${table.name}_id=:${table.name}_id";\n');
+ }
+
/// Returns the name including the names of the parent
@override
String fullName() => name;
TableModel table = tables[0];
if (columnName.contains('.')) {
final parts = columnName.split('.');
- table = tables.firstWhere((element) => element.name == parts[0]);
+ table = tables.firstWhere((element) => element.name == parts[0],
+ orElse: () => null);
if (table == null) {
logger.error(
'unknown reference (table) "$columnName" in ${caller.fullName()}');
/// Returns the main table of the module.
/// This is the first defined table.
TableModel mainTable() {
- TableModel rc = tables.length == 0 ? null : tables[0];
+ final rc = tables.isEmpty ? null : tables[0];
return rc;
}
/// Returns a child page given by [name], null otherwise.
PageModel pageByName(String name) {
- final found = pages.firstWhere((element) => element.name == name);
+ final found = pages.firstWhere((element) => element.name == name,
+ orElse: () => null);
return found;
}
/// Returns a child table given by [name], null otherwise.
TableModel tableByName(String name) {
- final found = tables.firstWhere((element) => element.name == name);
+ final found = tables.firstWhere((element) => element.name == name,
+ orElse: () => null);
return found;
}
/// Parses the map and stores the data in the instance.
void parse() {
- name = parseString('name', map, required: true);
+ name = parseString('page', map, required: true);
checkSuperfluousAttributes(
- map, 'name options pageType sections'.split(' '));
+ map, 'options page pageType sections'.split(' '));
pageModelType = parseEnum<PageModelType>(
'pageType', map, PageModelType.values,
required: true);
"module": "role",
"tables": [
{
- 'name': 'role',
+ 'table': 'role',
'columns': [
{
- 'name': 'role_id',
+ 'column': 'role_id',
'dataType': 'int',
'label': 'Id',
'options': 'primary',
},
{
- 'name': 'role_name',
+ 'column': 'role_name',
'dataType': 'string',
'label': 'Rolle',
'size': 32,
'options': 'unique;notnull',
},
{
- 'name': 'role_priority',
+ 'column': 'role_priority',
'dataType': 'int',
'label': 'Priorität',
},
{
- 'name': 'role_created',
- 'dataType': 'dateTime',
- 'label': 'Erzeugt',
- 'options': 'hidden;null',
- },
- {
- 'name': 'role_changed',
- 'dataType': 'dateTime',
- 'label': 'Geändert',
- 'options': 'hidden;null',
+ 'column': 'role_active',
+ 'dataType': 'bool',
+ 'label': 'Aktiv',
},
]
},
],
'pages': [
{
- "name": "create",
+ "page": "create",
"pageType": "create",
"sections": [
{
]
},
{
- "name": "change",
+ "page": "change",
"pageType": "change",
"sections": [
{
]
},
{
- "name": "list",
+ "page": "list",
"pageType": "list",
"sections": [
{
"module": "user",
"tables": [
{
- 'name': 'users',
+ 'table': 'user',
'columns': [
{
- 'name': 'user_id',
+ 'column': 'user_id',
'dataType': 'int',
'label': 'Id',
'options': 'primary',
},
{
- 'name': 'user_name',
+ 'column': 'user_name',
'dataType': 'string',
'label': 'User',
'size': 64,
- 'options': 'unique',
+ 'options': 'unique;notnull',
},
{
- 'name': 'user_displayname',
+ 'column': 'user_displayname',
'dataType': 'string',
'label': 'Anzeigename',
'size': 32,
'options': 'unique',
},
{
- 'name': 'user_email',
+ 'column': 'user_email',
'dataType': 'string',
'label': 'EMail',
'size': 128,
'options': 'unique',
},
{
- 'name': 'user_password',
+ 'column': 'user_password',
'dataType': 'string',
'label': 'User',
'size': 128,
'options': 'password',
},
{
- 'name': 'user_role',
+ 'column': 'user_role',
'dataType': 'reference',
'label': 'Role',
'foreignKey': 'role.role_id',
],
'pages': [
{
- "name": "create",
+ "page": "create",
"pageType": "create",
"sections": [
{
"sectionType": "simpleForm",
"children": [
{
- "widgetType": "text",
- "text": "*_Erfassung eines neuen Benutzers:_*",
- "options": "rich",
- },
- {
- "widgetType": "emptyLine",
- },
+ "widgetType": "allDbFields",
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "page": "change",
+ "pageType": "change",
+ "sections": [
+ {
+ "sectionType": "simpleForm",
+ "children": [
{
- "widgetType": "textField",
- "name": "user",
- "label": "Benutzer",
- "options": "required;unique",
- },
+ "widgetType": "allDbFields",
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "page": "list",
+ "pageType": "list",
+ "sections": [
+ {
+ "sectionType": "filterPanel",
+ "children": [
{
"widgetType": "textField",
- "name": "displayname",
- "label": "Anzeigename",
- "fieldType": "text",
- "options": "required",
+ "filterType": "pattern",
+ "name": "user_name",
},
{
"widgetType": "combobox",
- "name": "role",
- "label": "Rolle",
- "dataType": "reference",
- "options": "required;undef",
- },
+ "filterType": "equals",
+ "name": "user_role",
+ }
]
}
]
},
- ],
+ ]
};
UserModel(BaseLogger logger) : super(model, logger);
import 'column_model.dart';
import 'model_base.dart';
+import 'model_types.dart';
import 'module_model.dart';
/// Represents a database table describing the data model of the model.
/// Parses the map and stores the data in the instance.
void parse() {
- name = parseString('name', map, required: true);
- checkSuperfluousAttributes(map, 'name options columns'.split(' '));
+ name = parseString('table', map, required: true);
+ checkSuperfluousAttributes(map, 'columns options table'.split(' '));
options = parseOptions('options', map);
if (!map.containsKey('columns')) {
logger.error('missing columns in table ${fullName()}');
}
}
checkOptionsByRegExpr(options, regExprOptions);
+ _addIfMissing('${name}_createdat', 'Erzeugt', DataType.dateTime,
+ ['hidden', 'null', 'doStore']);
+ _addIfMissing('${name}_createdby', 'Erzeugt von', DataType.string,
+ ['hidden', 'doStore'], 16);
+ _addIfMissing('${name}_changedat', 'Geändert', DataType.dateTime,
+ ['hidden', 'null', 'doStore']);
+ _addIfMissing('${name}_changedby', 'Geändert von', DataType.string,
+ ['hidden', 'doStore'], 16);
}
@override
widgetName() => name;
+ /// Adds a column [name] if it does not exist with [label] and [dataType].
+ void _addIfMissing(
+ String name, String label, DataType dataType, List<String> options,
+ [int size]) {
+ if (getColumn(name, required: false) == null) {
+ addColumn(ColumnModel.raw(
+ name: name,
+ table: this,
+ label: label,
+ dataType: dataType,
+ options: options ?? [],
+ size: size,
+ logger: logger));
+ }
+ }
+
/// Returns a list of tables constructed by the Json like [list].
static void parseList(
ModuleModel module, List<dynamic> list, BaseLogger logger) {
FormFieldSetter onSaved;
final Map<String, dynamic> map;
- FilterType filterType;
TextFieldModel(
SectionModel section, PageModel page, this.map, BaseLogger logger)
.split(' '));
maxSize = parseInt('maxSize', map, required: false);
rows = parseInt('rows', map, required: false);
- filterType = parseEnum<FilterType>('filterType', map, FilterType.values);
switch (dataType) {
case DataType.int:
case DataType.reference:
/// Describes a text widget without user interaction.
class TextModel extends WidgetModel {
- static final regExprOptions = RegExp(r'^(richtext)$');
+ static final regExprOptions = RegExp(r'^(rich)$');
List<String> options;
String text;
bool isRichText;
: super(logger) {
this.id = ++lastId;
}
-
/// Dumps the internal structure into a [stringBuffer]
StringBuffer dump(StringBuffer stringBuffer);
+++ /dev/null
-import 'package:dart_bones/dart_bones.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_bones/flutter_bones.dart';
-import 'package:test/test.dart';
-
-import '../model/standard/role_model.dart';
-import '../model/standard/user_model.dart';
-
-void dummy() {
- group('xxx', () {
- test('y', () {
- Widget text = Text('');
- expect(text, isNotNull);
- });
- });
-}
-
-void main(List<String> argv) async {
- final logger = MemoryLogger(LEVEL_FINE);
- if (argv.length > 0xffff) {
- dummy();
- }
- final dbHelper = DbHelper(logger);
- if (argv.length == 0) {
- logger.error("missing arguments");
- } else {
- final mode = argv[0];
- argv.removeAt(0);
- switch (mode) {
- case 'create-sql':
- dbHelper.exportSql(argv);
- break;
- default:
- logger.error('unknown mode: ${argv[0]}');
- break;
- }
- }
- //await tool.main();
-}
-
-class DbHelper {
- final BaseLogger logger;
-
- DbHelper(this.logger);
-
- void exportSql(List<String> argv) {
- if (argv.length == 0) {
- logger.error('missing table');
- } else {
- final table = argv[0];
- argv.removeAt(0);
- ModuleModel module;
- switch (table) {
- case 'user':
- module = UserModel(logger);
- break;
- case 'role':
- module = RoleModel(logger);
- break;
- default:
- logger.error('unknown table');
- break;
- }
- final filename = argv.length == 0 ? '$table.sql' : argv[0];
- FileSync.toFile(filename, module.exportSql());
- print('exported: $filename');
- }
- }
-
- void usage(String error) {
- print('''usage: dbhelper <mode> [<args>]
-<mode>:
- create-sql <module> [<output-file>]
- <module>: 'role', 'user' ...
-Examples:
-dbhelper create-sql role role.sql
-''');
- }
-}
void add(FilterItem item) => filters.add(item);
FilterItem byName(String name) =>
- filters.firstWhere((element) => element.name == name);
+ filters.firstWhere((element) => element.name == name, orElse: () => null);
@override
getOnChanged(String customString, TextCallbackController controller) {
sdk: flutter
flutter_localizations:
sdk: flutter
- dart_bones: "^0.2.2"
+ dart_bones: "^0.4.3"
# The following adds the Cupertino Icons font to your application.
import 'package:flutter_bones/flutter_bones.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart';
import 'package:test/test.dart';
void main() {
+ setUpAll(() {
+ Intl.defaultLocale = "de_DE";
+ initializeDateFormatting();
+ });
group('fromString', () {
test('fromString-string', () {
expect(StringHelper.fromString('abc', DataType.string), equals('abc'));
startsWith('<StringHelper.fromString(): unknown datatype'));
});
});
+ group('dateXToString', () {
+ test('dateToString', () {
+ expect(StringHelper.dateToString(DateTime(2020, 10, 7), sortable: true),
+ equals('2020.10.07'));
+ expect(StringHelper.dateToString(DateTime(2020, 10, 7), sortable: false),
+ equals('7.10.2020'));
+ expect(StringHelper.dateToString(null, sortable: true).contains('.'),
+ isTrue);
+ });
+ test('dateTimeToString', () {
+ expect(
+ StringHelper.dateTimeToString(DateTime(2020, 10, 7, 4, 33, 2),
+ sortable: true, separator: '-', withSeconds: true),
+ equals('2020.10.07-04:33:02'));
+ expect(
+ StringHelper.dateTimeToString(DateTime(2020, 10, 7, 4, 33, 2),
+ sortable: true, separator: '-', withSeconds: false),
+ equals('2020.10.07-04:33'));
+ expect(
+ StringHelper.dateTimeToString(DateTime(2020, 10, 7, 4, 33, 2),
+ sortable: false),
+ equals('7.10.2020 04:33:02'));
+ expect(
+ StringHelper.dateTimeToString(DateTime(2020, 10, 7, 4, 33, 2),
+ sortable: false, withSeconds: false),
+ equals('7.10.2020 04:33'));
+ expect(
+ StringHelper.dateTimeToString(null,
+ sortable: false, withSeconds: false)
+ .contains(' '),
+ isTrue);
+ });
+ });
+ group("diverse", () {
+ test('splitArgs', () {
+ var args = ['a', '-a', 'b'];
+ var options = <String>[];
+ expect(StringHelper.splitArgv(args, options), equals(['a', 'b']));
+ expect(options, equals(['-a']));
+ options.clear();
+ args = ['-a', '--b=c'];
+ expect(StringHelper.splitArgv(args, options), equals([]));
+ expect(options, equals(args));
+ options.clear();
+ args = ['a'];
+ expect(StringHelper.splitArgv(args, options), equals(args));
+ expect(options, equals([]));
+ options.clear();
+ args = [];
+ expect(StringHelper.splitArgv(args, options), equals(args));
+ expect(options, equals([]));
+ });
+ });
}
final page = module.pageByName('create');
expect(page?.fullName(), equals('demo1.create'));
expect(module.fullName(), equals('demo1'));
- final table = module.tableByName('users');
- expect(table.fullName(), equals('demo1.users'));
- expect(table.widgetName(), equals('users'));
+ final table = module.tableByName('user');
+ expect(table.fullName(), equals('demo1.user'));
+ expect(table.widgetName(), equals('user'));
final dump = module.dump(StringBuffer()).toString();
expect(dump, '''= module demo1: options:
-== table users: options:
- column user_id: DataType.int "Id" options: primary
+== table user: options:
+ column user_id: DataType.int "Id" options: primary notnull unique
column user_name: DataType.string "User" options: unique
column user_role: DataType.reference "Role" options:
-== table roles: options:
- column role_id: DataType.int "Id" options: primary
+ column user_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
+ column user_createdby: DataType.string "Erzeugt von" options: hidden doStore
+ column user_changedat: DataType.dateTime "Geändert" options: hidden null doStore
+ column user_changedby: DataType.string "Geändert von" options: hidden doStore
+== table role: options:
+ column role_id: DataType.int "Id" options: primary notnull unique
column role_name: DataType.string "Role" options: unique
+ column role_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
+ column role_createdby: DataType.string "Erzeugt von" options: hidden doStore
+ column role_changedat: DataType.dateTime "Geändert" options: hidden null doStore
+ column role_changedby: DataType.string "Geändert von" options: hidden doStore
== page create: PageModelType.create options:
= section simpleForm1: SectionModelType.simpleForm options: [
textField user: options: required unique
final userField = table.getColumn('user_id');
expect(userField, isNotNull);
expect(userField.widgetName(), 'user_id');
- expect(userField.fullName(), equals('users.user_id'));
+ expect(userField.fullName(), equals('user.user_id'));
final userRef = page.getField('user');
expect(userRef, isNotNull);
expect(userRef.fullName(), equals('create.user'));
'module': 'demo1',
'tables': [
{
- 'name': 'users',
+ 'table': 'user',
'columns': [
{
- 'name': 'user_id',
+ 'column': 'user_id',
'dataType': 'int',
'label': 'Id',
'options': 'primary',
},
{
- 'name': 'user_name',
+ 'column': 'user_name',
'dataType': 'string',
'label': 'User',
'size': 32,
'options': 'unique',
},
{
- 'name': 'user_role',
+ 'column': 'user_role',
'dataType': 'reference',
'label': 'Role',
'foreignKey': 'role.role_id',
]
},
{
- 'name': 'roles',
+ 'table': 'role',
'columns': [
{
- 'name': 'role_id',
+ 'column': 'role_id',
'dataType': 'int',
'label': 'Id',
'options': 'primary',
},
{
- 'name': 'role_name',
+ 'column': 'role_name',
'dataType': 'string',
'label': 'Role',
'size': 32,
],
'pages': [
{
- 'name': 'create',
+ 'page': 'create',
'pageType': 'create',
'sections': [
{
{
'widgetType': 'dbReference',
'name': 'role',
- 'column': 'roles.role_id',
+ 'column': 'role.role_id',
},
{
'widgetType': 'button',
var module = Demo1(map, logger);
module.parse();
var errors = logger.errors;
- expect(errors.length, equals(3));
+ expect(errors.length, equals(2));
final dump = module.dump(StringBuffer()).toString();
expect(dump, equals('''= module demo1: options:
== page create: PageModelType.create options:
expect(nonFieldWidgets.length, equals(2));
});
});
+ group('standard-models', () {
+ test('user', () {
+ final model = UserModel(logger);
+ model.parse();
+ });
+ });
}
final userModel = <String, dynamic>{
'module': 'demo1',
'pages': [
{
- 'name': 'create',
+ 'page': 'create',
'pageType': 'create',
'sections': [
{
void main() {
final logger = MemoryLogger(LEVEL_FINE);
group('sql', () {
- test('export-sql', () {
+ test('exportSqlCreateTablel', () {
logger.clear();
final module = RoleModel(logger);
module.parse();
- final content = module.exportSql();
+ final content = module.exportSqlCreateTable();
final errors = logger.errors;
expect(errors.length, equals(0));
- expect(content, equals('''drop table if exists role;
-create table role (
- role_id int(10) primary;
- role_name varchar(255) unique notnull;
- role_priority int(10);
- role_created timestamp null;
- role_changed timestamp null;
+ expect(content, equals('''DROP TABLE IF EXISTS role;
+CREATE TABLE role (
+ role_id INT(10) notnull unique,
+ role_name VARCHAR(32) unique notnull,
+ role_priority INT(10),
+ role_active CHAR(1),
+ role_createdat TIMESTAMP null,
+ role_createdby VARCHAR(16),
+ role_changedat TIMESTAMP null,
+ role_changedby VARCHAR(16),
+ PRIMARY KEY(role_id)
);
+'''));
+ });
+ test('exportSqlBackend', () {
+ logger.clear();
+ final module = RoleModel(logger);
+ module.parse();
+ final content = module.exportSqlBackend(testDate: true);
+ final errors = logger.errors;
+ expect(errors.length, equals(0));
+ expect(content, equals('''---
+# configuration of the bones backend for role:
+created: 2020.01.01 00:00:00
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+ - module: role
+ list:
+ - name: insert
+ type: insert
+ sql: "INSERT INTO role(role_name,role_priority,role_active,role_createdat,role_createdby,role_changedat,role_changedby)
+ VALUES(:role_name,:role_priority,:role_active,NOW(),:role_createdby,:role_changedat,:role_changedby);"
+ - name: update
+ type: update
+ sql: "UPDATE role SET
+ role_name=:role_name,role_priority=:role_priority,role_active=:role_active,role_createdat=:role_createdat,role_createdby=:role_createdby,role_changedat=NOW(),role_changedby=:role_changedby
+ WHERE role_id=:role_id";
+ - name: delete
+ type: delete
+ sql: "DELETE from role WHERE role_id=:role_id;"
+ - name: record
+ type: record
+ sql: "SELECT * from role WHERE role_id=:role_id;"
+ - name: list
+ type: list
+ sql: "SELECT * from role
+ WHERE role_name like :role_name;"
'''));
});
});