]> gitweb.hamatoma.de Git - flutter_bones.git/commitdiff
daily work:
authorHamatoma <author@hamatoma.de>
Mon, 19 Oct 2020 06:05:46 +0000 (08:05 +0200)
committerHamatoma <author@hamatoma.de>
Mon, 19 Oct 2020 06:10:04 +0000 (08:10 +0200)
28 files changed:
data/ddl/role.sql [new file with mode: 0644]
data/ddl/user.sql [new file with mode: 0644]
data/rest/role.yaml [new file with mode: 0644]
data/rest/user.yaml [new file with mode: 0644]
lib/flutter_bones.dart
lib/src/controller/base_controller.dart [new file with mode: 0644]
lib/src/controller/button_controller.dart [new file with mode: 0644]
lib/src/controller/combobox_controller.dart [new file with mode: 0644]
lib/src/controller/text_form_controller.dart [new file with mode: 0644]
lib/src/helper/settings.dart
lib/src/helper/validators.dart
lib/src/model/button_model.dart
lib/src/model/combobox_model.dart
lib/src/model/field_model.dart
lib/src/model/module_model.dart
lib/src/model/standard/configuration_model.dart [new file with mode: 0644]
lib/src/persistence/persistence.dart [new file with mode: 0644]
lib/src/persistence/rest_persistence.dart [new file with mode: 0644]
lib/src/widget/dropdown_button_form_bone.dart [new file with mode: 0644]
lib/src/widget/filters.dart
lib/src/widget/module_controller.dart
lib/src/widget/text_form_field_bone.dart
lib/src/widget/view.dart
test/helpers/settings_test.dart
test/model/model_test.dart
test/model/standard_test.dart [new file with mode: 0644]
test/rest_persistence_test.dart [new file with mode: 0644]
test/tool/tool_test.dart

diff --git a/data/ddl/role.sql b/data/ddl/role.sql
new file mode 100644 (file)
index 0000000..73b38db
--- /dev/null
@@ -0,0 +1,12 @@
+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 role_id
+);
diff --git a/data/ddl/user.sql b/data/ddl/user.sql
new file mode 100644 (file)
index 0000000..1b4d6b7
--- /dev/null
@@ -0,0 +1,14 @@
+drop table if exists user;
+create table user (
+  user_id int(10) notnull unique,
+  user_name varchar(64) unique notnull,
+  user_displayname varchar(32) unique,
+  user_email varchar(128) unique,
+  user_password varchar(128),
+  user_role int(10),
+  user_createdat timestamp null,
+  user_createdby varchar(16),
+  user_changedat timestamp null,
+  user_changedby varchar(16),
+  primary user_id
+);
diff --git a/data/rest/role.yaml b/data/rest/role.yaml
new file mode 100644 (file)
index 0000000..cd080f3
--- /dev/null
@@ -0,0 +1,23 @@
+---
+# configuration of the bones backend for role:
+created: 2020.10.17 01:05:34
+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;"
diff --git a/data/rest/user.yaml b/data/rest/user.yaml
new file mode 100644 (file)
index 0000000..4145774
--- /dev/null
@@ -0,0 +1,23 @@
+---
+# configuration of the bones backend for user:
+created: 2020.10.17 01:05:34
+author: flutter_bones.module_model.exportSqlBackend()
+version: 1.0.0
+modules:
+  - module: user
+    list:
+      - name: insert
+        type: insert
+        sql: "INSERT INTO user(user_name,user_displayname,user_email,user_password,user_role,user_createdat,user_createdby,user_changedat,user_changedby)
+          VALUES(:user_name,:user_displayname,:user_email,:user_password,:user_role,NOW(),:user_createdby,:user_changedat,:user_changedby);"
+      - name: update
+        type: update
+        sql: "UPDATE user SET
+          user_name=:user_name,user_displayname=:user_displayname,user_email=:user_email,user_password=:user_password,user_role=:user_role,user_createdat=:user_createdat,user_createdby=:user_createdby,user_changedat=NOW(),user_changedby=:user_changedby
+          WHERE user_id=:user_id";
+      - name: delete
+        type: delete
+        sql: "DELETE from user WHERE user_id=:user_id;"
+      - name: record
+        type: record
+        sql: "SELECT * from user WHERE user_id=:user_id;"
index 0d8fa51acfc7625634985437dbb183e42d4678af..da5cb5ed915ccdfa9b3315eea489864e1240e6ef 100644 (file)
@@ -15,6 +15,7 @@ export 'src/model/model_types.dart';
 export 'src/model/module_model.dart';
 export 'src/model/page_model.dart';
 export 'src/model/section_model.dart';
+export 'src/model/standard/configuration_model.dart';
 export 'src/model/standard/role_model.dart';
 export 'src/model/standard/user_model.dart';
 export 'src/model/text_field_model.dart';
@@ -24,6 +25,8 @@ export 'src/page/login_page.dart';
 export 'src/page/page_data.dart';
 export 'src/page/role/role_create_page.dart';
 export 'src/page/user_page.dart';
+export 'src/persistence/persistence.dart';
+export 'src/persistence/rest_persistence.dart';
 export 'src/widget/raised_button_bone.dart';
 export 'src/widget/simple_form.dart';
 export 'src/widget/text_form_field_bone.dart';
diff --git a/lib/src/controller/base_controller.dart b/lib/src/controller/base_controller.dart
new file mode 100644 (file)
index 0000000..46b9533
--- /dev/null
@@ -0,0 +1,11 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+/// Base class of all controller classes in this package.
+abstract class BaseController {
+  /// Returns the assigned model.
+  WidgetModel getModel();
+
+  /// Returns a unique name of the controller inside the module, mostly the
+  /// widget name of the assigned model.
+  String getName();
+}
diff --git a/lib/src/controller/button_controller.dart b/lib/src/controller/button_controller.dart
new file mode 100644 (file)
index 0000000..7d5347c
--- /dev/null
@@ -0,0 +1,48 @@
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_bones/src/controller/base_controller.dart';
+
+import '../widget/raised_button_bone.dart';
+
+class ButtonController extends BaseController
+    implements ButtonCallbackController {
+  ButtonModel model;
+
+  ButtonController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnHighlightChanged(
+      String customString, ButtonCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnLongPressed(String customString, ButtonCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnPressed(String customString, ButtonCallbackController controller) {
+    var rc;
+    switch (model.buttonModelType) {
+      case ButtonModelType.cancel:
+        break;
+      case ButtonModelType.custom:
+        break;
+      case ButtonModelType.search:
+        break;
+      case ButtonModelType.store:
+        break;
+    }
+    return rc;
+  }
+}
diff --git a/lib/src/controller/combobox_controller.dart b/lib/src/controller/combobox_controller.dart
new file mode 100644 (file)
index 0000000..322f642
--- /dev/null
@@ -0,0 +1,57 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/dropdown_button_form_bone.dart';
+import 'base_controller.dart';
+
+class ComboboxController<T> extends BaseController
+    implements ComboboxCallbackController {
+  ComboboxModel model;
+
+  ComboboxController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnChanged(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSaved(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSelectedItemBuilder(
+      String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnTap(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnValidator(String customString, ComboboxCallbackController controller) {
+    return null;
+  }
+
+  @override
+  List<String> texts() {
+    return model.texts;
+  }
+
+  @override
+  List<dynamic> values() {
+    return model.values;
+  }
+}
diff --git a/lib/src/controller/text_form_controller.dart b/lib/src/controller/text_form_controller.dart
new file mode 100644 (file)
index 0000000..d7cd775
--- /dev/null
@@ -0,0 +1,53 @@
+import 'package:flutter_bones/flutter_bones.dart';
+
+import '../widget/text_form_field_bone.dart';
+import 'base_controller.dart';
+
+class TextFormController extends BaseController
+    implements TextFormCallbackController {
+  ComboboxModel model;
+
+  TextFormController(this.model);
+
+  @override
+  WidgetModel getModel() {
+    return model;
+  }
+
+  @override
+  String getName() {
+    return model.widgetName();
+  }
+
+  @override
+  getOnChanged(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnEditingComplete(
+      String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnFieldSubmitted(
+      String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnSaved(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getOnTap(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+
+  @override
+  getValidator(String customString, TextFormCallbackController controller) {
+    return null;
+  }
+}
index 2836e36395cb04cac6066900784c0891da677ab5..a3dfb5afce22809f32f8f16514a39807e7602b7c 100644 (file)
@@ -1,39 +1,36 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
+import 'package:intl/date_symbol_data_local.dart';
+import 'package:intl/intl.dart';
 
-class Settings {
+class BaseSettings {
   static Locale locale = Locale('US', 'en');
 
-  static final mapWidgetData = <String, dynamic>{
-    'form.card.padding': '16.0',
-    'form.gap.field_button.height': '16.0',
-  };
-  static Settings _instance;
+  static get language => locale.languageCode;
 
-  final BaseConfiguration widgetConfiguration;
+  final BaseConfiguration configuration;
 
   final BaseLogger logger;
 
-  factory Settings({BaseLogger logger, BaseConfiguration widgetConfiguration}) {
-    if (_instance == null) {
-      _instance = Settings.internal(
-          widgetConfiguration == null
-              ? BaseConfiguration(mapWidgetData, logger)
-              : widgetConfiguration,
-          logger);
-    }
-    return _instance;
-  }
-
-  Settings.internal(this.widgetConfiguration, this.logger);
+  BaseSettings(this.configuration, this.logger);
 
   /// Sets the locale code.
   /// [locale] the info about localisation
   /// Finding [locale] of an app:  @see https://github.com/flutter/website/blob/master/examples/internationalization/minimal/lib/main.dart
-  static setLocale(locale) => Settings.locale = locale;
+  static setLocale(locale) {
+    BaseSettings.locale = locale;
+    Intl.defaultLocale = "${locale.languageCode}_$locale.countryCode";
+    initializeDateFormatting();
+  }
 
-  static setLocaleByNames({String country = 'US', String language = 'en'}) =>
-      Settings.locale = Locale(country, language);
+  /// Sets the locales with [language] and [country].
+  static setLocaleByNames({String country, @required String language}) {
+    country ??= language;
+    country = country.toUpperCase();
+    country = country == 'EN' ? 'US' : country;
+    language = language.toLowerCase();
+    setLocale(Locale(language, country));
+  }
 
   /// Translates a [text] with a given translation [map].
   /// Structure of the [map]: { <English text> : { <language code> : translation } }
@@ -52,3 +49,25 @@ class Settings {
     return rc;
   }
 }
+
+class Settings extends BaseSettings {
+  static final mapWidgetData = <String, dynamic>{
+    'form.card.padding': '16.0',
+    'form.gap.field_button.height': '16.0',
+  };
+  static Settings _instance;
+
+  factory Settings({BaseLogger logger, BaseConfiguration widgetConfiguration}) {
+    if (_instance == null) {
+      _instance = Settings.internal(
+          widgetConfiguration == null
+              ? BaseConfiguration(mapWidgetData, logger)
+              : widgetConfiguration,
+          logger);
+    }
+    return _instance;
+  }
+
+  Settings.internal(BaseConfiguration configuration, BaseLogger logger)
+      : super(configuration, logger);
+}
index 7bb567b19f7c4a0508852921544756529012f0dd..369f054f022587eb8366e7e849edfb2390a606a3 100644 (file)
@@ -46,7 +46,7 @@ String checkPhoneNumber(String input) => Validation.isPhoneNumber(input)
         {'0': input});
 
 String _vt(String key, [Map<String, String> placeholders]) =>
-    Settings.translate(key, ValidatorTranslations.translations,
+    BaseSettings.translate(key, ValidatorTranslations.translations,
         placeholders: placeholders);
 
 @protected
index eb5721c9a700b971d9546b82068fbf12da70b415..3fba73b8e2e486211eae53cc21e4a2a09228aa9e 100644 (file)
@@ -5,7 +5,7 @@ import 'package:flutter_bones/flutter_bones.dart';
 typedef ButtonOnPressed = void Function(String name);
 
 /// Describes a button widget.
-class ButtonModel extends WidgetModel implements ButtonCallbackController {
+class ButtonModel extends WidgetModel {
   static final regExprOptions = RegExp(r'^(undef)$');
   String text;
   String name;
@@ -48,22 +48,6 @@ class ButtonModel extends WidgetModel implements ButtonCallbackController {
 
   @override
   String widgetName() => name;
-
-  @override
-  getOnHighlightChanged(
-      String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnLongPressed(String customString, ButtonCallbackController controller) {
-    return null;
-  }
-
-  @override
-  getOnPressed(String customString, ButtonCallbackController controller) {
-    return onPressed;
-  }
 }
 
 enum ButtonModelType {
index 395c660974b528d7ba17840b0d130bb0d552622e..695a47dab396e49b1da13e294c112da968763a6f 100644 (file)
@@ -32,7 +32,7 @@ class ComboboxModel extends FieldModel {
     super.parse();
     checkSuperfluousAttributes(
         map,
-        'name label dataType options texts toolTip widgetType values'
+        'name label dataType filterType options texts toolTip widgetType values'
             .split(' '));
     texts = parseStringList('texts', map);
     values = parseValueList('values', map, dataType);
index d9185fbf4dcfeef4e084e8f027968b3924cfdf6f..5d5b0c823dd8bda0e901c8d3625e80c10af214ea 100644 (file)
@@ -9,8 +9,6 @@ import 'widget_model.dart';
 
 /// Base class of widgets with user interaction like text field, combobox, checkbox.
 abstract class FieldModel extends WidgetModel {
-  static final regExprOptions =
-      RegExp(r'^(undef|readonly|disabled|password|required)$');
   String name;
   String label;
   String toolTip;
index 2d781e2dd9f386879c3842ac95410b6283f85cd3..6e6bb38a0e589dd9ee9c6c406f42645c10c6cd54 100644 (file)
@@ -119,6 +119,9 @@ class ModuleModel extends ModelBase {
             case FilterType.dateTimeTil:
               buffer.write('${field.name}<=:${field.name}');
               break;
+            case FilterType.equals:
+              buffer.write('${field.name} like :${field.name}');
+              break;
             case FilterType.pattern:
               buffer.write('${field.name} like :${field.name}');
               break;
@@ -192,7 +195,7 @@ modules:
             type = 'DOUBLE';
             break;
           case DataType.int:
-            type = 'INT(10)';
+            type = column.hasOption('primary') ? 'INT(10) UNSIGNED' : 'INT(10)';
             break;
           case DataType.reference:
             type = 'INT(10) UNSIGNED';
@@ -211,10 +214,15 @@ modules:
         }
         String options = '';
         for (var option in column.options) {
-          if ('notnull null unique'.contains(option)) {
-            options += ' ' + option;
+          if (option == 'notnull') {
+            options += ' NOT NULL';
+          } else if ('null unique'.contains(option)) {
+            options += ' ' + option.toUpperCase();
           }
         }
+        if (column.hasOption('primary')) {
+          options += ' AUTO_INCREMENT';
+        }
         buffer.write('  ${column.name} $type$options,\n');
       }
       buffer.write('  PRIMARY KEY(${table.name}_id)\n');
diff --git a/lib/src/model/standard/configuration_model.dart b/lib/src/model/standard/configuration_model.dart
new file mode 100644 (file)
index 0000000..fcc66a6
--- /dev/null
@@ -0,0 +1,122 @@
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+
+///
+class ConfigurationModel extends ModuleModel {
+  static final yamlMap = <String, dynamic>{
+    //
+    "module": "configuration",
+    "tables": [
+      {
+        // configuration_id | configuration_scope | configuration_property | configuration_order | configuration_type
+        // | configuration_value | configuration_description
+        'table': 'configuration',
+        'columns': [
+          {
+            'column': 'configuration_id',
+            'dataType': 'int',
+            'label': 'Id',
+            'options': 'primary',
+          },
+          {
+            'column': 'configuration_scope',
+            'dataType': 'string',
+            'label': 'Bereich',
+            'size': 64,
+            'options': 'unique;notnull',
+          },
+          {
+            'column': 'configuration_property',
+            'dataType': 'string',
+            'size': 64,
+            'label': 'Eigenschaft',
+          },
+          {
+            'column': 'configuration_order',
+            'dataType': 'int',
+            'label': 'Reihe',
+          },
+          {
+            'column': 'configuration_type',
+            'dataType': 'string',
+            'size': 16,
+            'label': 'Datentyp',
+          },
+          {
+            'column': 'configuration_value',
+            'dataType': 'string',
+            'size': 255,
+            'label': 'Wert',
+          },
+          {
+            'column': 'configuration_description',
+            'dataType': 'string',
+            'size': 255,
+            'label': 'Beschreibung',
+          },
+        ]
+      },
+    ],
+    'pages': [
+      {
+        "page": "create",
+        "pageType": "create",
+        "sections": [
+          {
+            "sectionType": "simpleForm",
+            "children": [
+              {
+                "widgetType": "allDbFields",
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "page": "change",
+        "pageType": "change",
+        "sections": [
+          {
+            "sectionType": "simpleForm",
+            "children": [
+              {
+                "widgetType": "allDbFields",
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "page": "list",
+        "pageType": "list",
+        "sections": [
+          {
+            "sectionType": "filterPanel",
+            "children": [
+              {
+                "name": "configuration_name",
+                "widgetType": "textField",
+                "filterType": "pattern",
+              },
+              {
+                "name": "configuration_scope",
+                "widgetType": "combobox",
+                "filterType": "equals",
+                "options": 'undef',
+              },
+            ]
+          }
+        ]
+      },
+    ],
+  };
+
+  ConfigurationModel(BaseLogger logger) : super(yamlMap, logger);
+
+  /// Returns the name including the names of the parent
+  @override
+  String fullName() => name;
+
+  @override
+  String widgetName() => name;
+}
diff --git a/lib/src/persistence/persistence.dart b/lib/src/persistence/persistence.dart
new file mode 100644 (file)
index 0000000..c50ce3d
--- /dev/null
@@ -0,0 +1,30 @@
+import 'package:meta/meta.dart';
+
+abstract class Persistence {
+  /// Deletes a record with primary key [id] of the [module] with the
+  /// SQL statement [sqlName].
+  Future delete({@required String module, String sqlName, @required int id});
+
+  /// Inserts a record of the [module] with the SQL statement [sqlName]
+  /// and returns the primary key.
+  Future<int> insert(
+      {@required String module,
+      String sqlName,
+      @required Map<String, dynamic> data});
+
+  /// Returns all records of the [module] specified with [params] using
+  /// a SQL statement named [sqlName].
+  Future<List<Map<String, dynamic>>> list(
+      {@required String module, String sqlName, Map<String, dynamic> params});
+
+  /// Returns a record with primary key [id] of the [module] with the
+  /// SQL statement [sqlName].
+  Future<Map<String, dynamic>> record(
+      {@required String module, String sqlName, @required int id});
+
+  /// Updates a record of [module] with the [data] using the SQL statement [sqlName].
+  Future update(
+      {@required String module,
+      String sqlName,
+      @required Map<String, dynamic> data});
+}
diff --git a/lib/src/persistence/rest_persistence.dart b/lib/src/persistence/rest_persistence.dart
new file mode 100644 (file)
index 0000000..2542e92
--- /dev/null
@@ -0,0 +1,139 @@
+import 'dart:convert' as convert;
+
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/src/persistence/persistence.dart';
+import 'package:http/http.dart' as http;
+import 'package:meta/meta.dart';
+
+/// A persistence layer using a REST interface to store/fetch the data.
+class RestPersistence extends Persistence {
+  static RestPersistence _instance;
+  final String application;
+  final String version;
+  final BaseLogger logger;
+  final String host;
+
+  final String scheme;
+  int port;
+  int sessionTimeout;
+  String uriPrefix;
+
+  Map<String, String> headers;
+
+  factory RestPersistence(
+      {String application,
+      String version,
+      int port,
+      String host,
+      BaseLogger logger}) {
+    _instance ??= RestPersistence.internal(
+        application: application,
+        host: host,
+        port: port,
+        version: version,
+        logger: logger);
+    return _instance;
+  }
+
+  factory RestPersistence.fromConfig(
+      BaseConfiguration configuration, BaseLogger logger) {
+    final section = 'client';
+    _instance = RestPersistence.internal(
+        application: configuration.asString('application', section: section),
+        version: configuration.asString('version', section: section),
+        host: configuration.asString('host', section: section),
+        port: configuration.asInt('port', section: section),
+        scheme: configuration.asString('schema',
+            section: section, defaultValue: 'https'),
+        logger: logger);
+
+    return _instance;
+  }
+
+  RestPersistence.internal(
+      {this.application,
+      @required this.version,
+      @required this.host,
+      @required this.port,
+      this.scheme = 'http',
+      @required this.logger}) {
+    uriPrefix = '$scheme://$host:$port';
+  }
+
+  @override
+  Future delete({String module, String sqlName, int id}) async {
+    sqlName ??= 'delete';
+    await runRequest(module, sqlName, 'delete', body: '{${module}_id:$id}');
+  }
+
+  @override
+  Future<int> insert(
+      {String module, String sqlName, Map<String, dynamic> data}) async {
+    sqlName ??= 'insert';
+    var rc = 0;
+    final data2 = data == null ? '{}' : convert.jsonEncode(data);
+    final answer = await runRequest(module, sqlName, 'insert', body: data2);
+    if (answer.isNotEmpty) {
+      final map = convert.jsonDecode(answer);
+      rc = map['id'];
+    }
+    return rc;
+  }
+
+  /// Handles a HTTP request with a single HTTP connection.
+  /// [module]: the module which implies the requested table.
+  /// [sqlName]: the name of the SQL statement.
+  /// [sqlType]: 'insert', 'record', 'update', 'list', 'delete'
+  /// [body]: null or the body of the POST request, e.g. '{ 'role_id'
+  /// [headers]: the optional HTTP headers.
+  /// [result]: the body of the response or ''
+  Future<String> runRequest(String module, String sqlName, String sqlType,
+      {String body, Map<String, String> headers}) async {
+    var rc = '';
+    final uri = '$uriPrefix/$application/$module/$sqlType/$sqlName/$version';
+    http.Response response;
+    logger.log('request: POST $module $sqlName', LEVEL_LOOP);
+    response = await http.post(uri, body: body, headers: headers);
+    logger.log('status: ${response.statusCode}', LEVEL_LOOP);
+    if (response.statusCode != 200) {
+      logger.error('$uri: status: ${response.statusCode}');
+    } else {
+      rc = response.body ?? '';
+    }
+    return rc;
+  }
+
+  @override
+  Future<List<Map<String, dynamic>>> list(
+      {String module, String sqlName, Map<String, dynamic> params}) async {
+    sqlName ??= 'list';
+    List<Map<String, dynamic>> rc;
+    final answer = await runRequest(module, sqlName, 'list',
+        body: params == null ? '{}' : convert.jsonEncode(params));
+    if (answer.isNotEmpty) {
+      rc = convert.jsonDecode(answer);
+    }
+    return rc;
+  }
+
+  @override
+  Future<Map<String, dynamic>> record(
+      {String module, String sqlName, int id}) async {
+    sqlName ??= 'record';
+    Map rc;
+    final answer =
+        await runRequest(module, sqlName, 'record', body: '{${module}_id:$id}');
+    if (answer.isNotEmpty) {
+      rc = convert.jsonDecode(answer);
+    }
+    return rc;
+  }
+
+  @override
+  Future update(
+      {String module, String sqlName, Map<String, dynamic> data}) async {
+    sqlName ??= 'update';
+    final data2 = data == null ? '{}' : convert.jsonEncode(data);
+    await runRequest(module, sqlName, 'record', body: data2);
+  }
+}
diff --git a/lib/src/widget/dropdown_button_form_bone.dart b/lib/src/widget/dropdown_button_form_bone.dart
new file mode 100644 (file)
index 0000000..3bbedf6
--- /dev/null
@@ -0,0 +1,86 @@
+import 'package:flutter/material.dart';
+
+/// Implements a [DropdownButtonFormField] with "outsourced" callbacks:
+/// [customString] a string mostly used for a name needed in the [customController]
+/// [callbackController] handles the callback methods.
+class DropdownButtonFormBone<T> extends DropdownButtonFormField<T> {
+  final String customString;
+  final ComboboxCallbackController callbackController;
+
+  DropdownButtonFormBone(
+    this.customString,
+    this.callbackController, {
+    Key key,
+    @required List<DropdownMenuItem<T>> items,
+    T value,
+    Widget hint,
+    Widget disabledHint,
+    int elevation: 8,
+    TextStyle style,
+    Widget icon,
+    Color iconDisabledColor,
+    Color iconEnabledColor,
+    double iconSize: 24.0,
+    bool isDense: true,
+    bool isExpanded: false,
+    double itemHeight,
+    Color focusColor,
+    FocusNode focusNode,
+    bool autofocus: false,
+    Color dropdownColor,
+    InputDecoration decoration,
+    FormFieldSetter<T> onSaved,
+    FormFieldValidator<T> validator,
+    AutovalidateMode autovalidateMode,
+  }) : super(
+          key: key,
+          items: items,
+          selectedItemBuilder: callbackController.getOnSelectedItemBuilder(
+              customString, callbackController),
+          value: value,
+          hint: hint,
+          disabledHint: disabledHint,
+          onChanged:
+              callbackController.getOnChanged(customString, callbackController),
+          onTap: callbackController.getOnTap(customString, callbackController),
+          elevation: elevation,
+          style: style,
+          icon: icon,
+          iconDisabledColor: iconDisabledColor,
+          iconEnabledColor: iconEnabledColor,
+          iconSize: iconSize,
+          isDense: isDense,
+          isExpanded: isExpanded,
+          itemHeight: itemHeight,
+          focusColor: focusColor,
+          focusNode: focusNode,
+          autofocus: autofocus,
+          dropdownColor: dropdownColor,
+          decoration: decoration,
+          autovalidateMode: autovalidateMode,
+        );
+}
+
+/// Interface for a [DropdownButtonFormBone] specific callback controller.
+abstract class ComboboxCallbackController<T> {
+  ValueChanged<T> getOnChanged(
+      String customString, ComboboxCallbackController controller);
+
+  FormFieldSetter<T> getOnSaved(
+      String customString, ComboboxCallbackController controller);
+
+  DropdownButtonBuilder getOnSelectedItemBuilder(
+      String customString, ComboboxCallbackController controller);
+
+  VoidCallback getOnTap(
+      String customString, ComboboxCallbackController controller);
+
+  FormFieldValidator<T> getOnValidator(
+      String customString, ComboboxCallbackController controller);
+
+  String getName();
+
+  List<String> texts();
+
+  List<dynamic> values();
+}
index 99e290fb1cc9e9a0349ae2fee6b17a0c5209211b..dd004e713e49d59aac05f3d1d75664128abb2c5f 100644 (file)
@@ -61,7 +61,7 @@ class FilterItem {
 
 class Filters
     implements
-        TextCallbackController,
+        TextFormCallbackController,
         ButtonCallbackController,
         TableCallbackController {
   var filters = <FilterItem>[];
@@ -80,12 +80,13 @@ class Filters
       filters.firstWhere((element) => element.name == name, orElse: () => null);
 
   @override
-  getOnChanged(String customString, TextCallbackController controller) {
+  getOnChanged(String customString, TextFormCallbackController controller) {
     return null;
   }
 
   @override
-  getOnEditingComplete(String customString, TextCallbackController controller) {
+  getOnEditingComplete(
+      String customString, TextFormCallbackController controller) {
     return null;
   }
 
@@ -96,7 +97,8 @@ class Filters
   }
 
   @override
-  getOnFieldSubmitted(String customString, TextCallbackController controller) {
+  getOnFieldSubmitted(String customString,
+      TextFormCallbackController controller) {
     return null;
   }
 
@@ -126,19 +128,19 @@ class Filters
   }
 
   @override
-  getOnSaved(String customString, TextCallbackController controller) {
+  getOnSaved(String customString, TextFormCallbackController controller) {
     return (input) {
       byName(customString).value = input;
     };
   }
 
   @override
-  getOnTap(String customString, TextCallbackController controller) {
+  getOnTap(String customString, TextFormCallbackController controller) {
     return null;
   }
 
   @override
-  getValidator(String customString, TextCallbackController controller) {
+  getValidator(String customString, TextFormCallbackController controller) {
     return null;
   }
 
index 49c10c8e3cd9451cbec2efa5ba30c929a7761046..6570b82b671670a31de7b62a163e9492128f549e 100644 (file)
@@ -11,7 +11,7 @@ import 'widget_helper.dart';
 
 // This interface allows the generic handling of the edit form by a model driven module.
 class ModuleController
-    implements TextCallbackController, ButtonCallbackController {
+    implements TextFormCallbackController, ButtonCallbackController {
   final ModuleModel moduleModel;
   String primaryColumn;
   WidgetList createWidgets;
@@ -33,17 +33,19 @@ class ModuleController
   ModuleModel getModuleModel() => moduleModel;
 
   @override
-  getOnChanged(String customString, TextCallbackController controller) {
+  getOnChanged(String customString, TextFormCallbackController controller) {
     return null;
   }
 
   @override
-  getOnEditingComplete(String customString, TextCallbackController controller) {
+  getOnEditingComplete(
+      String customString, TextFormCallbackController controller) {
     return null;
   }
 
   @override
-  getOnFieldSubmitted(String customString, TextCallbackController controller) {
+  getOnFieldSubmitted(String customString,
+      TextFormCallbackController controller) {
     return null;
   }
 
@@ -73,7 +75,7 @@ class ModuleController
   }
 
   @override
-  getOnSaved(String customString, TextCallbackController controller) {
+  getOnSaved(String customString, TextFormCallbackController controller) {
     final rc = (input) {
       values[customString] =
           StringHelper.fromString(input, dataTypes[customString]);
@@ -82,12 +84,12 @@ class ModuleController
   }
 
   @override
-  getOnTap(String customString, TextCallbackController controller) {
+  getOnTap(String customString, TextFormCallbackController controller) {
     return null;
   }
 
   @override
-  getValidator(String customString, TextCallbackController controller) {
+  getValidator(String customString, TextFormCallbackController controller) {
     return null;
   }
 
index e8c01ded21f50098d5ef7570bfeb03a92fba5521..e3a963466aaa0e0e42d7e7881270884fee35403a 100644 (file)
@@ -2,24 +2,24 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
 /// Interface for a [TextFormField] specific callback controller.
-abstract class TextCallbackController {
+abstract class TextFormCallbackController {
   ValueChanged<String> getOnChanged(
-      String customString, TextCallbackController controller);
+      String customString, TextFormCallbackController controller);
 
   VoidCallback getOnEditingComplete(
-      String customString, TextCallbackController controller);
+      String customString, TextFormCallbackController controller);
 
   ValueChanged<String> getOnFieldSubmitted(
-      String customString, TextCallbackController controller);
+      String customString, TextFormCallbackController controller);
 
   FormFieldSetter<String> getOnSaved(
-      String customString, TextCallbackController controller);
+      String customString, TextFormCallbackController controller);
 
-  GestureTapCallback getOnTap(
-      String customString, TextCallbackController controller);
+  GestureTapCallback getOnTap(String customString,
+      TextFormCallbackController controller);
 
-  FormFieldValidator<String> getValidator(
-      String customString, TextCallbackController controller);
+  FormFieldValidator<String> getValidator(String customString,
+      TextFormCallbackController controller);
 }
 
 /// Implements a [TextFormField] with "outsourced" callbacks:
@@ -27,17 +27,16 @@ abstract class TextCallbackController {
 /// [callbackController] handles the callback methods.
 class TextFormFieldBone extends TextFormField {
   final String customString;
-  final TextCallbackController callbackController;
+  final TextFormCallbackController callbackController;
 
-  TextFormFieldBone(
-    this.customString,
-    this.callbackController, {
-    Key key,
-    TextEditingController controller,
-    String initialValue,
-    FocusNode focusNode,
-    InputDecoration decoration = const InputDecoration(),
-    TextInputType keyboardType,
+  TextFormFieldBone(this.customString,
+      this.callbackController, {
+        Key key,
+        TextEditingController controller,
+        String initialValue,
+        FocusNode focusNode,
+        InputDecoration decoration = const InputDecoration(),
+        TextInputType keyboardType,
     TextCapitalization textCapitalization = TextCapitalization.none,
     TextInputAction textInputAction,
     TextStyle style,
index d820d8fe2aa57b762578c0069bd81c62e5984356..50b8144c30437e7e47940196dc1603d6da1efa5b 100644 (file)
@@ -1,14 +1,16 @@
 import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter_bones/src/controller/button_controller.dart';
 
+import '../controller/combobox_controller.dart';
 import '../helper/settings.dart';
-import '../model/button_model.dart';
 import '../model/empty_line_model.dart';
 import '../model/field_model.dart';
 import '../model/section_model.dart';
 import '../model/text_field_model.dart';
 import '../model/text_model.dart';
 import '../model/widget_model.dart';
+import 'dropdown_button_form_bone.dart';
 import 'raised_button_bone.dart';
 
 class View {
@@ -22,32 +24,46 @@ class View {
     if (_instance == null) {
       _instance = View.internal(logger);
       _instance.settings = Settings(logger: logger);
-      _instance.widgetConfiguration = _instance.settings.widgetConfiguration;
+      _instance.widgetConfiguration = _instance.settings.configuration;
     }
     return _instance;
   }
 
   View.internal(this.logger);
 
-  /// Creates a button from the [model].
-  Widget button(ButtonModel model) {
+  /// Creates a button from the [controller].
+  Widget button(ButtonController controller) {
     final rc = RaisedButtonBone(
-      model.widgetName(),
-      model,
-      child: Text(model.text),
+      controller.getName(),
+      controller,
+      child: Text(controller.model.text),
     );
     return rc;
   }
 
-  /// Creates a list of buttons from a list of [models].
-  List<Widget> buttonList(List<WidgetModel> models) {
+  /// Creates a list of buttons from a list of [controllers].
+  List<Widget> buttonList(List<ButtonController> controllers) {
     final rc = <Widget>[];
-    for (var item in models) {
+    for (var item in controllers) {
       rc.add(button(item));
     }
     return rc;
   }
 
+  /// Creates a combobox via the [controller].
+  Widget combobox<T>(ComboboxController controller, onTap) {
+    final texts = controller.texts();
+    final values = controller.values() ?? texts;
+    final items = <DropdownMenuItem<T>>[];
+    for (var ix = 0; ix < texts.length; ix++) {
+      items.add(DropdownMenuItem(
+          onTap: onTap, value: values[ix], child: Text(texts[ix])));
+    }
+    final rc =
+        DropdownButtonFormBone(controller.getName(), controller, items: items);
+    return rc;
+  }
+
   /// Creates a checkbox from the [model].
   Widget checkbox(WidgetModel model) {
     var rc;
@@ -82,9 +98,10 @@ class View {
   Form simpleForm({SectionModel model, Key formKey}) {
     assert(formKey != null);
     final padding =
-        widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
+    widgetConfiguration.asFloat('form.card.padding', defaultValue: 16.0);
     final children = widgetsOfSection(model.children);
-    final buttons = buttonList(model.buttons);
+    final controllers = model.buttons.map((button) => ButtonController(button));
+    final buttons = buttonList(controllers);
     final rc = Form(
         key: formKey,
         child: Card(
@@ -142,7 +159,7 @@ class View {
           rc.add(textField(child));
           break;
         case WidgetModelType.button:
-          rc.add(button(child));
+          rc.add(button(ButtonController(child)));
           break;
         case WidgetModelType.emptyLine:
           rc.add(emptyLine(child));
index 5413fd67a31128d549d4faeb288325c2f3531c7b..dda85c856ab6e2004cab12bb04263c9e7acf8013 100644 (file)
@@ -1,37 +1,47 @@
+import 'package:dart_bones/dart_bones.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bones/flutter_bones.dart';
+import 'package:intl/intl.dart';
 import 'package:test/test.dart';
 
 void main() {
+  Intl.defaultLocale = "de_DE";
+  final logger = MemoryLogger(LEVEL_FINE);
   final map = {
     'help': {'de': 'Hilfe'},
-    'wrong name %{0}': {'de': 'falscher name %{0}'}
+    'wrong name %{0}': {'de': 'falscher Name %{0}'}
   };
-  setUpAll(() => Settings.setLocaleByNames(language: 'en'));
+  setUpAll(() => BaseSettings.setLocaleByNames(language: 'en'));
   group('Settings', () {
     test('basic', () {
-      Settings.setLocale(Locale('de', 'de'));
-      expect(Settings.translate('abc', map), equals('abc'));
-      expect(Settings.translate('help', map), equals('help'));
+      BaseSettings.setLocale(Locale('de', 'DE'));
+      expect(BaseSettings.language, 'de');
+      expect(BaseSettings.translate('abc', map), equals('abc'));
+      expect(BaseSettings.translate('help', map), equals('Hilfe'));
       expect(
-          Settings.translate('wrong name %{0}', map,
+          BaseSettings.translate('wrong name %{0}', map,
               placeholders: {'0': 'Joe'}),
-          equals('wrong name Joe'));
+          equals('falscher Name Joe'));
+      expect(Settings(logger: logger), isNotNull);
     });
     test('translate-en', () {
-      Settings.setLocaleByNames(language: 'en');
-      expect(Settings.translate('abc', map), equals('abc'));
-      expect(Settings.translate('help', map), equals('help'));
+      BaseSettings.setLocaleByNames(language: 'en');
+      expect(BaseSettings.language, 'en');
+      expect(BaseSettings.translate('abc', map), equals('abc'));
+      expect(BaseSettings.translate('help', map), equals('help'));
       expect(
-          Settings.translate('wrong name %{0}', map,
+          BaseSettings.translate('wrong name %{0}', map,
               placeholders: {'0': 'Joe'}),
           equals('wrong name Joe'));
     });
     test('translate-de', () {
-      Settings.setLocaleByNames(language: 'de');
-      expect(Settings.translate('abc', map), equals('abc'));
-      expect(Settings.translate('help', map), equals('Hilfe'));
-      expect(Settings.translate('wrong name %{0}', map, placeholders: {'0' : 'Joe'}), equals('falscher name Joe'));
+      BaseSettings.setLocaleByNames(language: 'de');
+      expect(BaseSettings.translate('abc', map), equals('abc'));
+      expect(BaseSettings.translate('help', map), equals('Hilfe'));
+      expect(
+          BaseSettings.translate('wrong name %{0}', map,
+              placeholders: {'0': 'Joe'}),
+          equals('falscher Name Joe'));
     });
 });
 }
index 9e85c68a20dad909ffe65ea9142090f8772d5e11..a9bc06792051b2ea30a01691be5b57f5ab87c49f 100644 (file)
@@ -40,7 +40,7 @@ void main() {
     });
   });
   group('ModelBase', () {
-    test('errors', () {
+    test('basic', () {
       logger.clear();
       final map = cloneOfMap(userModel);
       final field = <String, dynamic>{
@@ -94,8 +94,31 @@ void main() {
     });
   });
   group('CheckboxModel', () {
-    logger.clear();
+    test('basic', () {
+      logger.clear();
+      final map = cloneOfMap(userModel);
+      final field = <String, dynamic>{
+        'widgetType': 'checkbox',
+        'name': 'hidden',
+        'label': 'Hidden',
+        'options': 'required',
+      };
+      map['pages'][0]['sections'][0]['children'][0] = field;
+      var module = Demo1(map, logger);
+      module.parse();
+      var errors = logger.errors;
+      expect(errors.length, equals(0));
+      final checkbox = module.pageByName('create').getField('hidden');
+      expect(checkbox.hasOption('required'), isTrue);
+      checkbox.value = true;
+      expect(checkbox.value, isTrue);
+      checkbox.value = false;
+      expect(checkbox.value, isFalse);
+      checkbox.value = 'true';
+      expect(checkbox.value, isTrue);
+    });
     test('errors', () {
+      logger.clear();
       final map = cloneOfMap(userModel);
       final field = <String, dynamic>{
         'widgetType': 'checkbox',
@@ -107,7 +130,7 @@ void main() {
       var module = Demo1(map, logger);
       module.parse();
       var errors = logger.errors;
-      expect(errors.length, equals(2));
+      expect(errors.length, equals(0));
       final checkbox = module.pageByName('create').getField('hidden');
       expect(checkbox, isNotNull);
       expect(checkbox.dataType, DataType.bool);
@@ -138,7 +161,7 @@ void main() {
       var module = Demo1(map, logger);
       module.parse();
       var errors = logger.errors;
-      expect(errors.length, equals(2));
+      expect(errors.length, equals(0));
       final combobox = module.pageByName('create').getField('class');
       expect(combobox, isNotNull);
       expect(combobox.dataType, DataType.int);
@@ -153,8 +176,8 @@ void main() {
     });
   });
   group('Non field widgets', () {
-    logger.clear();
     test('basic', () {
+      logger.clear();
       final map = cloneOfMap(userModel);
       final list = [
         {
@@ -166,15 +189,9 @@ void main() {
       var module = Demo1(map, logger);
       module.parse();
       var errors = logger.errors;
-      expect(errors.length, equals(2));
+      expect(errors.length, equals(0));
       final dump = module.dump(StringBuffer()).toString();
-      expect(dump, equals('''= module demo1: options: 
-== page create: PageModelType.create options: 
-    = section simpleForm1: SectionModelType.simpleForm options:  [
-    emptyLine: options: 
-    text 24 text: *Hi world*: options: rich
-    ] # create.simpleForm1
-'''));
+      expect(dump.contains('text: *Hi world*: options: rich'), isTrue);
       final page = module.pageByName('create');
       final allWidgets = page.getWidgets(null);
       expect(allWidgets.length, equals(2));
@@ -190,12 +207,6 @@ void main() {
       expect(nonFieldWidgets.length, equals(2));
     });
   });
-  group('standard-models', () {
-    test('user', () {
-      final model = UserModel(logger);
-      model.parse();
-    });
-  });
 }
 
 final userModel = <String, dynamic>{
diff --git a/test/model/standard_test.dart b/test/model/standard_test.dart
new file mode 100644 (file)
index 0000000..27dd8b1
--- /dev/null
@@ -0,0 +1,114 @@
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:test/test.dart';
+
+void main() {
+  final logger = MemoryLogger(LEVEL_FINE);
+  group('module', () {
+    test('role', () {
+      logger.clear();
+      final module = RoleModel(logger);
+      module.parse();
+      expect(module.fullName(), equals('role'));
+      expect(module.widgetName(), equals('role'));
+      final errors = logger.errors;
+      expect(errors.length, equals(0));
+      final dump = module.dump(StringBuffer()).toString();
+      expect(dump, '''= module role: options: 
+== table role: options: 
+    column role_id: DataType.int "Id" options: primary notnull unique
+    column role_name: DataType.string "Rolle" options: unique notnull
+    column role_priority: DataType.int "Priorität" options: 
+    column role_active: DataType.bool "Aktiv" options: 
+    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:  [
+    allDbFields 10 text: options: 
+    ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 12 text: options: 
+    ] # change.simpleForm1
+== page list: PageModelType.list options: 
+    = section filterPanel1: SectionModelType.filterPanel options:  [
+    textField role_name: options: 
+    ] # list.filterPanel1
+''');
+    });
+    test('user', () {
+      logger.clear();
+      final module = UserModel(logger);
+      module.parse();
+      expect(module.fullName(), equals('user'));
+      expect(module.widgetName(), equals('user'));
+      final errors = logger.errors;
+      expect(errors.length, equals(0));
+      final dump = module.dump(StringBuffer()).toString();
+      expect(dump, '''= module user: options: 
+== table user: options: 
+    column user_id: DataType.int "Id" options: primary notnull unique
+    column user_name: DataType.string "User" options: unique notnull
+    column user_displayname: DataType.string "Anzeigename" options: unique
+    column user_email: DataType.string "EMail" options: unique
+    column user_password: DataType.string "User" options: password
+    column user_role: DataType.reference "Role" options: 
+    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
+== page create: PageModelType.create options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 26 text: options: 
+    ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields 28 text: options: 
+    ] # change.simpleForm1
+== page list: PageModelType.list options: 
+    = section filterPanel1: SectionModelType.filterPanel options:  [
+    textField user_name: options: 
+    combobox user_role: texts:  options: 
+    ] # list.filterPanel1
+''');
+    });
+    test('configuration', () {
+      logger.clear();
+      final module = ConfigurationModel(logger);
+      module.parse();
+      expect(module.fullName(), equals('configuration'));
+      expect(module.widgetName(), equals('configuration'));
+      final errors = logger.errors;
+      expect(errors.length, equals(0));
+      final dump = module.dump(StringBuffer()).toString();
+      expect(dump, startsWith('''= module configuration: options: 
+== table configuration: options: 
+    column configuration_id: DataType.int "Id" options: primary notnull unique
+    column configuration_scope: DataType.string "Bereich" options: unique notnull
+    column configuration_property: DataType.string "Eigenschaft" options: 
+    column configuration_order: DataType.int "Reihe" options: 
+    column configuration_type: DataType.string "Datentyp" options: 
+    column configuration_value: DataType.string "Wert" options: 
+    column configuration_description: DataType.string "Beschreibung" options: 
+    column configuration_createdat: DataType.dateTime "Erzeugt" options: hidden null doStore
+    column configuration_createdby: DataType.string "Erzeugt von" options: hidden doStore
+    column configuration_changedat: DataType.dateTime "Geändert" options: hidden null doStore
+    column configuration_changedby: DataType.string "Geändert von" options: hidden doStore
+== page create: PageModelType.create options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields '''));
+      /*
+      expect(dump.contains('''text: options:
+    ] # create.simpleForm1
+== page change: PageModelType.change options: 
+    = section simpleForm1: SectionModelType.simpleForm options:  [
+    allDbFields '''), isTrue);
+      */
+      expect(dump,
+          contains('combobox configuration_scope: texts:  options: undef'));
+      expect(dump, contains('textField configuration_name: options:'));
+    });
+  });
+}
diff --git a/test/rest_persistence_test.dart b/test/rest_persistence_test.dart
new file mode 100644 (file)
index 0000000..8130fa9
--- /dev/null
@@ -0,0 +1,41 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility that Flutter provides. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:dart_bones/dart_bones.dart';
+import 'package:flutter_bones/flutter_bones.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() async {
+  final logger = MemoryLogger(LEVEL_FINE);
+  setUpAll(() {
+    final configuration = BaseConfiguration({
+      'client': {
+        'host': 'localhost',
+        'port': 58011,
+        'schema': 'http',
+        'application': 'unittest',
+        'version': '1.0.0',
+      }
+    }, logger);
+    RestPersistence.fromConfig(configuration, logger);
+  });
+  group('queries', () {
+    test('list', () async {
+      final rest = RestPersistence();
+      final list = await rest
+          .list(module: 'role', sqlName: 'list', params: {'role_name': 'A'});
+      expect(list.length, equals(1));
+      expect(list[0].containsKey('role_id'), isTrue);
+    });
+    test('record', () async {
+      final rest = RestPersistence();
+      final record = await rest.record(module: 'role', id: 2);
+      expect(record.length, greaterThan(5));
+      expect(record.containsKey('role_id'), isTrue);
+    });
+  });
+}
index 023c4664090e4d2e7e8a1aa5af504d6118fcb821..746a6cc3b1c61a71a3dbd5e90a0f77903e1f04aa 100644 (file)
@@ -14,13 +14,13 @@ void main() {
       expect(errors.length, equals(0));
       expect(content, equals('''DROP TABLE IF EXISTS role;
 CREATE TABLE role (
-  role_id INT(10) notnull unique,
-  role_name VARCHAR(32) unique notnull,
+  role_id INT(10) UNSIGNED NOT NULL UNIQUE AUTO_INCREMENT,
+  role_name VARCHAR(32) UNIQUE NOT NULL,
   role_priority INT(10),
   role_active CHAR(1),
-  role_createdat TIMESTAMP null,
+  role_createdat TIMESTAMP NULL,
   role_createdby VARCHAR(16),
-  role_changedat TIMESTAMP null,
+  role_changedat TIMESTAMP NULL,
   role_changedby VARCHAR(16),
   PRIMARY KEY(role_id)
 );