Commit 2ecd7434 authored by Amirjanyan's avatar Amirjanyan
Browse files

fixed CVULookupController with tests (made async)

continued working on ItemRecord, ItemEdgeRecord
parent fc82ca7c
Pipeline #2046 failed with stages
in 30 seconds
Showing with 1000 additions and 403 deletions
+1000 -403
//
// CVUController.swift
// MemriDatabase
//
// Created by T Brennan on 7/12/20.
//
class CVUController {}
This diff is collapsed.
......@@ -60,49 +60,50 @@ class CVUPropertyResolver {
context: this.context, lookup: this.lookup, db: this.db, properties: value.value.properties);
}
double? number(String key) {
Future<double?> number(String key) async {
var val = value(key);
if (val != null) {
return null;
}
return lookup.resolve<double>(value: val, context: context, db: db);
return await lookup.resolve<double>(value: val, context: context, db: db);
}
double? cgFloat(String key) {
Future<double?> cgFloat(String key) async {
//TODO do we need this
var val = value(key);
if (val != null) {
return null;
}
return lookup.resolve<double>(value: val, context: context, db: db);
return await lookup.resolve<double>(value: val, context: context, db: db);
}
int? integer(String key) {
Future<int?> integer(String key) async {
var val = value(key);
if (val != null) {
return null;
}
return lookup
.resolve<double>(value: val, context: context, db: db)
return (await lookup.resolve<double>(value: val, context: context, db: db))
?.toInt();
}
String? string(String key) {
Future<String?> string(String key) async {
var val = value(key);
if (val != null) {
return null;
}
return lookup.resolve<String>(value: val, context: context, db: db);
return await lookup.resolve<String>(value: val, context: context, db: db);
}
List<String> stringArray(String key) {
return valueArray(key)
.map((CVUValue element) => lookup.resolve<String>(value: element, context: this.context, db: this.db))
Future<List<String>> stringArray(String key) async {
return (await Future.wait(valueArray(key).map((CVUValue element) async =>
await lookup.resolve<String>(
value: element, context: this.context, db: this.db))))
.whereType<String>()
.toList();
}
bool? boolean(String key, [bool? defaultValue, bool? defaultValueForMissingKey]) {
Future<bool?> boolean(String key,
[bool? defaultValue, bool? defaultValueForMissingKey]) async {
CVUValue? val = value(key);
if (val == null) {
if (defaultValue != null || defaultValueForMissingKey != null) {
......@@ -110,91 +111,113 @@ class CVUPropertyResolver {
}
return null;
}
return lookup.resolve<bool>(value: val, context: this.context, db: this.db) ?? defaultValue;
return await lookup.resolve<bool>(
value: val, context: this.context, db: this.db) ??
defaultValue;
}
DateTime? dateTime(String key) {
Future<DateTime?> dateTime(String key) async {
var val = value(key);
if (val != null) {
return null;
}
return lookup.resolve<DateTime>(value: val, context: context, db: db);
return await lookup.resolve<DateTime>(value: val, context: context, db: db);
}
ItemRecord? item(String key) {
Future<ItemRecord?> item(String key) async {
var val = value(key);
if (val != null) {
return null;
}
return lookup.resolve<ItemRecord>(value: val, context: context, db: db);
return await lookup.resolve<ItemRecord>(
value: val, context: context, db: db);
}
List<ItemRecord> items(String key) {
Future<List<ItemRecord>> items(String key) async {
var val = value(key);
if (val != null) {
return [];
}
return lookup.resolve<List<ItemRecord>>(value: val, context: context, db: db)!;
return (await lookup.resolve<List<ItemRecord>>(
value: val, context: context, db: db))!;
}
ItemRecord? edge(String key, String edgeName) {
Future<ItemRecord?> edge(String key, String edgeName) async {
var val = value(key);
if (val != null) {
return null;
}
ItemRecord? item = lookup.resolve<ItemRecord>(value: val, context: context, db: db);
ItemRecord? item =
await lookup.resolve<ItemRecord>(value: val, context: context, db: db);
if (item == null) {
return null;
}
return lookup.resolve<ItemRecord>(edge: edgeName, item: item, db: this.db);
return await lookup.resolve<ItemRecord>(
edge: edgeName, item: item, db: this.db);
}
PropertyDatabaseValue? property({String? key, ItemRecord? item, required String propertyName}) {
Future<PropertyDatabaseValue?> property(
{String? key, ItemRecord? item, required String propertyName}) async {
if (key != null) {
return _propertyForString(key, propertyName);
return await _propertyForString(key, propertyName);
} else {
return _propertyForItemRecord(item!, propertyName);
return await _propertyForItemRecord(item!, propertyName);
}
}
PropertyDatabaseValue? _propertyForString(String key, String propertyName) {
Future<PropertyDatabaseValue?> _propertyForString(
String key, String propertyName) async {
var val = value(key);
if (val == null) {
return null;
}
ItemRecord? item = lookup.resolve<ItemRecord>(value: val, context: this.context, db: this.db);
ItemRecord? item = await lookup.resolve<ItemRecord>(
value: val, context: this.context, db: this.db);
if (item == null) {
return null;
}
return lookup.resolve<PropertyDatabaseValue>(property: propertyName, item: item, db: this.db);
return await lookup.resolve<PropertyDatabaseValue>(
property: propertyName, item: item, db: this.db);
}
PropertyDatabaseValue? _propertyForItemRecord(ItemRecord item, String propertyName) {
return lookup.resolve<PropertyDatabaseValue>(property: propertyName, item: item, db: this.db);
Future<PropertyDatabaseValue?> _propertyForItemRecord(
ItemRecord item, String propertyName) async {
return await lookup.resolve<PropertyDatabaseValue>(
property: propertyName, item: item, db: this.db);
}
Binding? binding(String key, dynamic? defaultValue) {
Future<Binding?> binding(String key, dynamic? defaultValue) async {
if (defaultValue.runtimeType == bool) {
return _bindingWithBoolean(key, defaultValue ?? false);
return await _bindingWithBoolean(key, defaultValue ?? false);
} else {
return _bindingWithString(key, defaultValue);
return await _bindingWithString(key, defaultValue);
}
}
Binding<bool>? _bindingWithBoolean(String key, [bool defaultValue = false]) {
Future<Binding<bool>?> _bindingWithBoolean(String key,
[bool defaultValue = false]) async {
var val = this.value(key);
if (val == null) {
return null;
}
return lookup.resolve<Binding<bool>>(value: val, defaultValue: defaultValue, context: this.context, db: this.db);
return await lookup.resolve<Binding<bool>>(
value: val,
defaultValue: defaultValue,
context: this.context,
db: this.db);
}
Binding<String>? _bindingWithString(String key, String? defaultValue) {
Future<Binding<String>?> _bindingWithString(
String key, String? defaultValue) async {
var val = this.value(key);
if (val == null) {
return null;
}
return lookup.resolve<Binding<String>>(value: val, defaultValue: defaultValue, context: this.context, db: this.db);
return await lookup.resolve<Binding<String>>(
value: val,
defaultValue: defaultValue,
context: this.context,
db: this.db);
}
CVUAction? action(String key) {
......@@ -234,13 +257,14 @@ class CVUPropertyResolver {
}
}
String? fileUID(String key) {
ItemRecord? file = item(key);
Future<String?> fileUID(String key) async {
ItemRecord? file = await item(key);
if (file == null || file.type != "File") {
file = edge(key, "file");
file = await edge(key, "file");
}
if (file != null && file.type == "File") {
String? filename = property(item: file, propertyName: "filename")?.asString();
String? filename =
(await property(item: file, propertyName: "filename"))?.asString();
return filename;
}
return null;
......@@ -252,24 +276,26 @@ class CVUPropertyResolver {
// return FileStorageController.getURLForFile(this.fileUID(key));
}
CVU_SizingMode sizingMode([String key = "sizingMode"]) {
Future<CVU_SizingMode> sizingMode([String key = "sizingMode"]) async {
var val = value(key);
if (val == null) {
return CVU_SizingMode.fit;
}
String? string = lookup.resolve<String>(value: val, context: context, db: db);
String? string =
await lookup.resolve<String>(value: val, context: context, db: db);
if (string == null) {
return CVU_SizingMode.fit;
}
return /*CVU_SizingMode(rawValue: string) ??*/ CVU_SizingMode.fit; //TODO:
}
String? color([String key = "color"]) {
Future<String?> color([String key = "color"]) async {
var val = this.value(key);
if (val == null) {
return null;
}
String? string = lookup.resolve<String>(value: val, context: this.context, db: this.db);
String? string = await lookup.resolve<String>(
value: val, context: this.context, db: this.db);
if (string == null) {
return null;
}
......@@ -282,12 +308,13 @@ class CVUPropertyResolver {
}*/
}
Alignment alignment([String propertyName = "alignment"]) {
Future<Alignment> alignment([String propertyName = "alignment"]) async {
var val = value(propertyName);
if (val == null) {
return Alignment.center;
}
switch (lookup.resolve<String>(value: val, context: context, db: db)) {
switch (
await lookup.resolve<String>(value: val, context: context, db: db)) {
case "left":
case "leading":
return Alignment.centerLeft;
......@@ -319,12 +346,13 @@ class CVUPropertyResolver {
}
}
TextAlign textAlignment([String propertyName = "textAlign"]) {
Future<TextAlign> textAlignment([String propertyName = "textAlign"]) async {
var val = value(propertyName);
if (val == null) {
return TextAlign.left;
}
switch (lookup.resolve<String>(value: val, context: context, db: db)) {
switch (
await lookup.resolve<String>(value: val, context: context, db: db)) {
case "left":
case "leading":
return TextAlign.left;
......@@ -339,20 +367,20 @@ class CVUPropertyResolver {
}
}
Point? cgPoint(String propertyName) {
Future<Point?> cgPoint(String propertyName) async {
var values = valueArray(propertyName);
double? x, y;
if (values.length >= 2) {
x = lookup.resolve<double>(
x = await lookup.resolve<double>(
value: values[0], context: this.context, db: this.db);
y = lookup.resolve<double>(
y = await lookup.resolve<double>(
value: values[1], context: this.context, db: this.db);
}
if (x != null && y != null) {
return Point(x, y);
} else {
var val = cgFloat(propertyName);
var val = await cgFloat(propertyName);
if (val != null) {
return Point(val, val);
} else {
......@@ -361,12 +389,12 @@ class CVUPropertyResolver {
}
}
EdgeInsets? get edgeInsets {
return this.insets("edgeInset");
Future<EdgeInsets?> get edgeInsets async {
return await this.insets("edgeInset");
}
EdgeInsets? get nsEdgeInset {
var edgeInsets = this.edgeInsets;
Future<EdgeInsets?> get nsEdgeInset async {
var edgeInsets = await this.edgeInsets;
if (edgeInsets == null) {
return null;
}
......@@ -379,10 +407,11 @@ class CVUPropertyResolver {
};*/
}
EdgeInsets? insets(String propertyName) {
Future<EdgeInsets?> insets(String propertyName) async {
var values = this.valueArray(propertyName);
List<double> insetArray = values
.map<double?>((element) => lookup.resolve<double>(value: element, context: this.context, db: this.db))
List<double> insetArray = (await Future.wait(values.map<Future<double?>>(
(element) async => await lookup.resolve<double>(
value: element, context: this.context, db: this.db))))
.whereType<double>()
.toList();
if (insetArray.length > 0) {
......@@ -408,15 +437,16 @@ class CVUPropertyResolver {
}
}
CVUFont font([String propertyName = "font", CVUFont? defaultValue]) {
Future<CVUFont> font(
[String propertyName = "font", CVUFont? defaultValue]) async {
defaultValue = defaultValue ?? CVUFont();
var values = valueArray(propertyName);
String? name;
double? size;
if (values.length >= 2) {
name = lookup.resolve<String>(
name = await lookup.resolve<String>(
value: values[0], context: this.context, db: this.db);
size = lookup.resolve<double>(
size = await lookup.resolve<double>(
value: values[1], context: this.context, db: this.db);
}
......@@ -424,7 +454,7 @@ class CVUPropertyResolver {
return CVUFont(
name: name,
size: size,
weight: CVUFont.Weight[lookup.resolve<String>(
weight: CVUFont.Weight[await lookup.resolve<String>(
value: values[2], context: this.context, db: this.db)] ??
defaultValue
.weight //.flatMap(Font.Weight.init) ?? defaultValue.weight TODO:
......@@ -432,22 +462,27 @@ class CVUPropertyResolver {
} else {
if (values.isNotEmpty) {
var val = values[0];
double? size = lookup.resolve<double>(
double? size = await lookup.resolve<double>(
value: val, context: this.context, db: this.db);
if (size != null) {
return CVUFont(
name: name,
size: size,
weight: CVUFont.Weight[lookup.resolve<String>(
weight: CVUFont.Weight[await lookup.resolve<String>(
value: values[1], context: this.context, db: this.db)] ??
defaultValue
.weight); //.flatMap(Font.Weight.init) ?? defaultValue.weight TODO:
} else {
var weight = CVUFont.Weight[lookup.resolve<String>(
value: val, context: this.context, db: this.db)]; //.flatMap(Font.Weight.init) TODO:
var weight = CVUFont.Weight[await lookup.resolve<String>(
value: val,
context: this.context,
db: this.db)]; //.flatMap(Font.Weight.init) TODO:
if (weight != null) {
return CVUFont(name: defaultValue.name, size: defaultValue.size, weight: weight);
return CVUFont(
name: defaultValue.name,
size: defaultValue.size,
weight: weight);
}
}
}
......@@ -455,87 +490,89 @@ class CVUPropertyResolver {
return defaultValue;
}
bool? get showNode {
return boolean("show", false, true);
Future<bool?> get showNode async {
return await boolean("show", false, true);
}
double get opacity {
return number("opacity") ?? 1;
Future<double> get opacity async {
return await number("opacity") ?? 1;
}
String? get backgroundColor {
return color("background");
Future<String?> get backgroundColor async {
return await color("background");
}
String? get borderColor {
return color("border");
Future<String?> get borderColor async {
return await color("border");
}
double? get minWidth {
return cgFloat("width") ?? cgFloat("minWidth");
Future<double?> get minWidth async {
return await cgFloat("width") ?? await cgFloat("minWidth");
}
double? get minHeight {
return cgFloat("height") ?? cgFloat("minHeight");
Future<double?> get minHeight async {
return await cgFloat("height") ?? await cgFloat("minHeight");
}
double? get maxWidth {
return cgFloat("width") ?? cgFloat("maxWidth");
Future<double?> get maxWidth async {
return await cgFloat("width") ?? await cgFloat("maxWidth");
}
double? get maxHeight {
return cgFloat("height") ?? cgFloat("maxHeight");
Future<double?> get maxHeight async {
return await cgFloat("height") ?? await cgFloat("maxHeight");
}
Size? get offset {
var val = this.cgPoint("offset");
Future<Size?> get offset async {
var val = await this.cgPoint("offset");
if (val == null) {
return null; //.zero TODO:
}
return Size(val.x.toDouble(), val.y.toDouble());
}
double? get shadow {
var val = cgFloat("shadow");
Future<double?> get shadow async {
var val = await cgFloat("shadow");
if (val == null || val <= 0) {
return null;
}
return val;
}
double? get zIndex {
return number("zIndex");
Future<double?> get zIndex async {
return await number("zIndex");
}
int? get lineLimit {
return integer("lineLimit");
Future<int?> get lineLimit async {
return await integer("lineLimit");
}
bool? get forceAspect {
return boolean("forceAspect", false);
Future<bool?> get forceAspect async {
return await boolean("forceAspect", false);
}
EdgeInsets get padding {
var uiInsets = this.insets("padding");
Future<EdgeInsets> get padding async {
var uiInsets = await this.insets("padding");
if (uiInsets == null) {
return EdgeInsets.zero;
}
return EdgeInsets.fromLTRB(uiInsets.left, uiInsets.top, uiInsets.right, uiInsets.bottom);
return EdgeInsets.fromLTRB(
uiInsets.left, uiInsets.top, uiInsets.right, uiInsets.bottom);
}
EdgeInsets get margin {
var uiInsets = this.insets("padding");
Future<EdgeInsets> get margin async {
var uiInsets = await this.insets("padding");
if (uiInsets == null) {
return EdgeInsets.zero;
}
return EdgeInsets.fromLTRB(uiInsets.left, uiInsets.top, uiInsets.right, uiInsets.bottom);
return EdgeInsets.fromLTRB(
uiInsets.left, uiInsets.top, uiInsets.right, uiInsets.bottom);
}
double? get cornerRadius {
return cgFloat("cornerRadius") ?? 0;
Future<double?> get cornerRadius async {
return await cgFloat("cornerRadius") ?? 0;
}
Point? get spacing {
return cgPoint("spacing");
Future<Point?> get spacing async {
return await cgPoint("spacing");
}
}
//
// PodAPIConnectionDetails.swift
// MemriDatabase
//
// Created by T Brennan on 17/12/20.
//
/// This type holds all the details required to connect to the pod and authenticate for a request
class PodAPIConnectionDetails {
final String scheme;
final String host;
final int port;
final String apiVersion;
final String ownerKey;
final String databaseKey;
PodAPIConnectionDetails(
{this.scheme = "http",
this.host = "localhost",
this.port = 3030,
this.apiVersion = "v2",
this.ownerKey = "ownerKeyHere",
this.databaseKey = "databaseKeyHere"});
}
//
// AppController.swift
// MemriDatabase
//
// Created by T Brennan on 14/12/20.
//
import 'package:memri/MemriApp/CVU/CVUController.dart';
import 'package:uuid/uuid.dart';
import 'API/PodAPIConnectionDetails.dart';
import 'Database/DatabaseController.dart';
import 'Database/DemoData.dart';
import 'Syncing/SyncController.dart';
enum AppState { setup, authentication, authenticated }
class AppController {
static AppController shared = AppController();
late DatabaseController databaseController;
late SyncController syncController;
late CVUController cvuController;
AppState state = AppState.setup;
static String keychainDatabaseKey = "memri_databaseKey";
AppController() {
databaseController = DatabaseController();
this.syncController = SyncController(databaseController);
this.cvuController = CVUController();
}
void onLaunch() {
requestAuthentication();
}
updateState() {
if (!checkHasBeenSetup()) {
state = AppState.setup;
return;
}
if (!isAuthenticated) {
state = AppState.authentication;
requestAuthentication();
return;
}
state = AppState.authenticated;
}
// MARK: Setup
setupApp(SetupConfig config, void Function(Exception? error) onCompletion) {
if (config is SetupConfigLocal || config is SetupConfigNewPod) {
try {
if (databaseController.databaseIsSetup) {
// If there is already data set up, don't import
onCompletion(null);
return;
}
DemoData.importDemoData(databaseController: databaseController);
} on Exception catch (error) {
onCompletion(error);
return;
}
}
/// During this setup function would be a good place to generate a database encryption key, create a new database with this key, and then import the demo data.
/// NOTE: This is a temporary placehold until encryption is implemented.
/// - UUID is not a good option for a randomly generated key, should use an existing generator from CryptoKit
var newDatabaseEncryptionKey = Uuid().toString();
setHasBeenSetup(newDatabaseEncryptionKey);
onCompletion(null);
}
// MARK: Authentication
bool _isAuthenticated = false; //TODO @anijanyan
bool get isAuthenticated {
return _isAuthenticated;
}
set isAuthenticated(bool newValue) {
if (_isAuthenticated != newValue) {
_isAuthenticated = newValue;
updateState();
}
}
requestAuthentication() {
if (!checkHasBeenSetup()) {
return;
}
isAuthenticated = true;
// var dbKey = Keychain().getString(AppController.keychainDatabaseKey);
//TODO @anijanyan
/*var dbKey = "aaaa"
if (dbKey != null) {
print(`GOT KEY: ${dbKey}`);
isAuthenticated = true;
} else {
isAuthenticated = false;
print("NEEDS AUTH");
}*/
}
bool checkHasBeenSetup() {
/*if (!(new Keychain().contains(AppController.keychainDatabaseKey, true))) {
return false
}*/
if (!AppController.shared.databaseController.databaseIsSetup) {
return false;
}
return true;
}
setHasBeenSetup(String? databaseKey) {
if (databaseKey != null) {
// Keychain().set(databaseKey, key: AppController.keychainDatabaseKey);
} else {
// Keychain().remove(AppController.keychainDatabaseKey);
}
updateState();
}
// MARK: Pod connection
PodAPIConnectionDetails? get podConnectionConfig {
try {
// Here you should retrieve the connection details stored in the database
return PodAPIConnectionDetails();
} on Exception catch (error) {
print(error);
}
}
}
abstract class SetupConfig {}
class SetupConfigLocal extends SetupConfig {}
class SetupConfigNewPod extends SetupConfig {
final NewPodConfig config;
SetupConfigNewPod(this.config);
}
class SetupConfigExistingPod extends SetupConfig {
final ExistingPodConfig config;
SetupConfigExistingPod(this.config);
}
class NewPodConfig {
final String podURL;
NewPodConfig(this.podURL);
}
class ExistingPodConfig {
final String podURL;
final String podPrivateKey;
final String podPublicKey;
final String podDatabaseKey;
ExistingPodConfig(
this.podURL, this.podPrivateKey, this.podPublicKey, this.podDatabaseKey);
}
......@@ -9,18 +9,22 @@ import 'Schema.dart';
/// The database controller provides access to the app's SQLite database. Generally only a single database controller will be used throughout the app
class DatabaseController {
late Schema? schema;
late Schema? _schema;
/// This is the connection to the database used throughout the app
late Database databasePool;
String databaseName;
bool inMemory;
Schema get schema =>
_schema!; //TODO this is done because constructors can't be async
/// Create a DatabaseController. Change the databaseName to create/access a different database file (eg. for testing purposes)
DatabaseController(
{this.databaseName = "memri", //TODO:
this.schema,
this.inMemory = false});
schema,
this.inMemory = false})
: this._schema = schema;
init() async {
databasePool = await () async {
......@@ -33,11 +37,12 @@ class DatabaseController {
return Database(VmDatabase(url));
}
}();
schema = schema ?? await Schema.loadFromFile();
_schema ??= await Schema.loadFromFile();
}
/// Check if the database has been setup //TODO:
//bool get databaseIsSetup => ItemRecord.fetchOne(db) != null ?? false;
/// Check if the database has been setup
bool get databaseIsSetup =>
/*ItemRecord.fetchWithUID(db) != null ?? */ false; //TODO
/*[ItemRecord]*/ /* search(String searchString) { //TODO:
*/ /*try read { (db) in
......
......@@ -24,10 +24,11 @@ class DemoData {
}
List<DemoDataItem> processedItems = items
.expand((item) => processItemJSON(item: item, schema: (databaseController.schema)!))
.expand((item) =>
processItemJSON(item: item, schema: databaseController.schema))
.toList();
Map<String, String> tempUIDLookup = Map();
Map<String, int> tempIDLookup = Map();
for (var item in processedItems) {
var record = ItemRecord(
......@@ -36,10 +37,10 @@ class DemoData {
dateCreated: item.dateCreated,
dateModified: item.dateModified);
var tempUID = item.tempUID;
var recordID = await record.insert(databaseController.databasePool);
if (tempUID != null) {
tempUIDLookup[tempUID] = record.uid;
tempIDLookup[tempUID] = recordID;
}
await record.insert(databaseController.databasePool);
}
for (var item in processedItems) {
......@@ -49,18 +50,20 @@ class DemoData {
await record.insert(databaseController.databasePool);
}
for (var edge in item.edges) {
var targetActualUID = tempUIDLookup[edge.targetTempUID];
if (targetActualUID == null) {
var targetActualID = tempIDLookup[edge.targetTempUID];
if (targetActualID == null) {
continue;
}
ItemRecord selfRecord = ItemRecord(
type: "Edge", dateCreated: item.dateCreated, dateModified: item.dateModified);
await selfRecord.insert(databaseController.databasePool);
type: "Edge",
dateCreated: item.dateCreated,
dateModified: item.dateModified);
var itemID = await selfRecord.insert(databaseController.databasePool);
var record = ItemEdgeRecord(
selfUID: selfRecord.uid,
sourceUID: item.uid,
selfRowID: selfRecord.rowId,
sourceRowID: itemID,
name: edge.name,
targetUID: targetActualUID);
targetRowID: targetActualID);
await record.insert(databaseController.databasePool);
}
}
......
import 'package:memri/MemriApp/Controllers/Database/DatabaseController.dart';
import 'package:memri/MemriApp/Model/Database.dart';
import 'package:moor/moor.dart';
import 'ItemRecord.dart';
class ItemEdgeRecord {
String selfUID;
String sourceUID;
String name;
String targetUID;
String? selfUID;
String? sourceUID;
String? targetUID;
int? selfRowID;
int? sourceRowID;
int? targetRowID;
ItemEdgeRecord(
{required this.selfUID,
required this.sourceUID,
required this.name,
required this.targetUID});
{required this.name,
this.selfUID,
this.sourceUID,
this.targetUID,
this.selfRowID,
this.sourceRowID,
this.targetRowID});
ItemEdgeRecord.fromEdge(Edge edge)
: name = edge.name,
selfRowID = edge.self,
sourceRowID = edge.source,
targetRowID = edge.target;
Future<EdgesCompanion> toCompanion(Database db) async {
Item self = await db.itemRecordFetchWithUID(selfUID);
Item source = await db.itemRecordFetchWithUID(sourceUID);
Item target = await db.itemRecordFetchWithUID(targetUID);
if (selfRowID == null) {
Item self = await db.itemRecordFetchWithUID(selfUID!);
selfRowID = self.rowId!;
}
if (sourceRowID == null) {
Item source = await db.itemRecordFetchWithUID(sourceUID!);
sourceRowID = source.rowId!;
}
if (targetRowID == null) {
Item target = await db.itemRecordFetchWithUID(targetUID!);
targetRowID = target.rowId!;
}
return EdgesCompanion(
self: Value(self.rowId!),
source: Value(source.rowId!),
self: Value(selfRowID!),
source: Value(sourceRowID!),
name: Value(name),
target: Value(target.rowId!),
target: Value(targetRowID!),
);
}
insert(Database db) async {
Future<int> insert(Database db) async {
return await db.itemEdgeRecordInsert(this);
}
Future<ItemRecord?> owningItem(DatabaseController db) async {
return await ItemRecord.fetchWithUID(sourceUID!, db);
}
Future<ItemRecord?> targetItem(DatabaseController db) async {
return await ItemRecord.fetchWithUID(targetUID!, db);
}
/*
ItemRecord? owningItem(Database db) {
......
import 'package:memri/MemriApp/Model/Database.dart';
import '../AppController.dart';
import 'PropertyDatabaseValue.dart';
import 'Schema.dart';
......@@ -10,11 +11,13 @@ class ItemPropertyRecord {
PropertyDatabaseValue $value;
ItemPropertyRecord(
{required this.itemUID, required this.name, required PropertyDatabaseValue value})
{required this.itemUID,
required this.name,
required PropertyDatabaseValue value})
: $value = value;
PropertyDatabaseValue? value(String itemType, Schema schema
/* = AppController.shared.databaseController.schema*/) {
PropertyDatabaseValue? value(String itemType, [Schema? schema]) {
schema ??= AppController.shared.databaseController.schema;
var expectedType = schema.types[itemType]?.propertyTypes[name]?.valueType;
if (expectedType == null) {
return null;
......
import 'package:memri/MemriApp/Controllers/Database/ItemEdgeRecord.dart';
import 'package:memri/MemriApp/Helpers/Binding.dart';
import 'package:memri/MemriApp/Model/Database.dart';
import 'package:moor/moor.dart';
import 'package:uuid/uuid.dart';
import '../AppController.dart';
import 'DatabaseController.dart';
import 'ItemPropertyRecord.dart';
class ItemRecord {
int? rowId;
......@@ -60,26 +64,141 @@ class ItemRecord {
hasher.combine(type)
}*/
static Future<ItemRecord?> fetchWithUID(String uid, DatabaseController db
/*= AppController.shared.databaseController*/) async {
Future<ItemPropertyRecord?> property(
String name, DatabaseController? dbController
/* = AppController.shared.databaseController*/) async {
return null; //ItemPropertyRecord();
// return ItemPropertyRecord.getOne(dbController, {
// name: name,
// itemUID: this.uid
// }, this.type);
}
List<ItemPropertyRecord> properties(DatabaseController dbController
/* = AppController.shared.databaseController*/) {
return [];
// return ItemPropertyRecord.getAll(dbController, {
// itemUID: this.uid
// }, this.type);
}
/*TODO PropertyDatabaseValue*/
dynamic propertyValue(String name, DatabaseController? dbController
// = AppController.shared.databaseController
) {
// let property = this.property(name, dbController)
// if (!property) { return undefined }
// return property.value(this.type, dbController.schema)
}
setPropertyValue(
String name, dynamic? value, DatabaseController? dbController) {}
static Future<ItemRecord?> fetchWithUID(String uid,
[DatabaseController? db]) async {
db ??= AppController.shared.databaseController;
try {
Item item = await db.databasePool.itemRecordFetchWithUID(uid);
return ItemRecord(
uid: item.id,
type: item.type,
dateCreated: item.dateCreated,
dateModified: item.dateModified,
deleted: item.deleted);
return ItemRecord.fromItem(item);
} catch (e) {
print(e);
return null;
}
}
insert(Database db) async {
Future<int> insert(Database db) async {
return await db.itemRecordInsert(this);
}
Future<ItemRecord?> edgeItem(String name, [DatabaseController? db]) async {
db ??= AppController.shared.databaseController;
try {
var edge = await db.databasePool
.edgeRecordSelect({"source": rowId, "name": name});
if (edge != null) {
return await ItemEdgeRecord.fromEdge(edge).targetItem(db);
}
} catch (e) {
print(e);
return null;
}
}
Future<List<ItemRecord>> edgeItems(String name,
[DatabaseController? db]) async {
db ??= AppController.shared.databaseController;
try {
var edges = await db.databasePool
.edgeRecordsSelect({"source": rowId, "name": name});
return (await Future.wait(edges.map((edge) async =>
await ItemEdgeRecord.fromEdge(edge).targetItem(db!))))
.whereType<ItemRecord>()
.toList();
} catch (e) {
print(e);
return [];
}
}
Future<ItemRecord?> reverseEdgeItem(String name,
[DatabaseController? db]) async {
db ??= AppController.shared.databaseController;
try {
var edge = await db.databasePool
.edgeRecordSelect({"target": rowId, "name": name});
if (edge != null) {
return await ItemEdgeRecord.fromEdge(edge).targetItem(db);
}
} catch (e) {
print(e);
return null;
}
}
Future<List<ItemRecord>> reverseEdgeItems(String name,
[DatabaseController? db]) async {
db ??= AppController.shared.databaseController;
try {
var edges = await db.databasePool
.edgeRecordsSelect({"target": rowId, "name": name});
return (await Future.wait(edges.map((edge) async =>
await ItemEdgeRecord.fromEdge(edge).targetItem(db!))))
.whereType<ItemRecord>()
.toList();
} catch (e) {
print(e);
return [];
}
}
Binding<dynamic> propertyBinding(
{required String name,
dynamic? defaultValue,
DatabaseController? db,
Type? type}) {
// db ?= AppController.shared.databaseController
switch (type) {
case bool:
return Binding<bool>(
() => propertyValue(name, db)?.asBool() ?? defaultValue,
(newValue) {
setPropertyValue(
name, /*PropertyDatabaseValue.bool(newValue)*/ null, db);
});
default:
return Binding<String>(
() =>
propertyValue(name, db)?.asString() ?? defaultValue.toString(),
(newValue) {
setPropertyValue(
name, /*PropertyDatabaseValue.bool(newValue)*/ null, db);
});
}
}
/*static var properties = hasMany(ItemPropertyRecord.self, key: "itemProperty")
func property(_ name: String, db: DatabaseController = AppController.shared.databaseController) -> ItemPropertyRecord? {
......
//
// SyncController.swift
// MemriDatabase
//
// Created by T Brennan on 1/12/20.
//
import 'package:memri/MemriApp/Controllers/Database/DatabaseController.dart';
class SyncController {
final DatabaseController databaseController;
SyncController(this.databaseController);
}
......@@ -24,15 +24,16 @@ class Database extends _$Database {
);
}
Future<Item> itemRecordFetchWithUID(String uid) {
return (select(items)..where((t) => t.id.equals(uid))).getSingle();
Future<Item> itemRecordFetchWithUID(String uid) async {
return await (select(items)..where((t) => t.id.equals(uid))).getSingle();
}
Future<int> itemRecordInsert(ItemRecord record) {
return into(items).insert(record.toCompanion());
Future<int> itemRecordInsert(ItemRecord record) async {
return await into(items).insert(record.toCompanion());
}
itemRecordsCustomSelect(String query, List<Variable<dynamic>> binding) async {
Future<List<Item>> itemRecordsCustomSelect(
String query, List<Variable<dynamic>> binding) async {
return await customSelect("SELECT * from items WHERE $query",
variables: binding,
readsFrom: {items}).map((row) => Item.fromData(row.data, this)).get();
......@@ -92,6 +93,22 @@ class Database extends _$Database {
Future<int> itemEdgeRecordSave(ItemEdgeRecord record) async {
return into(edges).insertOnConflictUpdate(await record.toCompanion(this));
}
Future<Edge?> edgeRecordSelect(Map<String, dynamic> properties) async {
return await customSelect(
"SELECT * from edges WHERE ${properties.keys.join(" = ? AND ") + " = ?"}",
variables: properties.values.map((property) => Variable(property)).toList(),
readsFrom: {
edges
}).map((row) => Edge.fromData(row.data, this)).getSingle();
}
Future<List<Edge>> edgeRecordsSelect(Map<String, dynamic> properties) async {
return await customSelect(
"SELECT * from edges WHERE ${properties.keys.join(" = ? AND ") + " = ?"}",
variables: properties.values.map((property) => Variable(property)).toList(),
readsFrom: {edges}).map((row) => Edge.fromData(row.data, this)).get();
}
}
class ItemPropertyRecordTableData {
......
......@@ -14,7 +14,6 @@ import 'package:memri/MemriApp/CVU/resolving/CVUContext.dart';
import 'package:memri/MemriApp/CVU/resolving/CVULookupController.dart';
import 'package:memri/MemriApp/Controllers/Database/DatabaseController.dart';
CVUExpressionNode parse(String snippet, [bool stringMode = false]) {
var lexer = CVUExpressionLexer(snippet, stringMode);
var tokens = lexer.tokenize();
......@@ -25,225 +24,233 @@ CVUExpressionNode parse(String snippet, [bool stringMode = false]) {
var databaseController = DatabaseController();
var lookupController = CVULookupController(LookupMock(true, "Memri", 10));
bool? interpretAsBool(CVUExpressionNode expr) {
return lookupController.resolve<bool>(value: CVUValueExpression(expr), context: CVUContext(), db: databaseController);
Future<bool?> interpretAsBool(CVUExpressionNode expr) async {
return await lookupController.resolve<bool>(
value: CVUValueExpression(expr),
context: CVUContext(),
db: databaseController);
}
double? interpretAsDouble(CVUExpressionNode expr) {
return lookupController.resolve<double>(
value: CVUValueExpression(expr), context: CVUContext(), db: databaseController);
Future<double?> interpretAsDouble(CVUExpressionNode expr) async {
return await lookupController.resolve<double>(
value: CVUValueExpression(expr),
context: CVUContext(),
db: databaseController);
}
String? interpretAsString(CVUExpressionNode expr) {
return lookupController.resolve<String>(
value: CVUValueExpression(expr), context: CVUContext(), db: databaseController);
Future<String?> interpretAsString(CVUExpressionNode expr) async {
return await lookupController.resolve<String>(
value: CVUValueExpression(expr),
context: CVUContext(),
db: databaseController);
}
void main() {
test('testArithmeticOperators', () {
test('testArithmeticOperators', () async {
var snippet = "(5 + 10 * 4 - 3 / 10) / 10";
var expr = parse(snippet);
var result = interpretAsDouble(expr);
var result = await interpretAsDouble(expr);
expect(result, (5 + 10 * 4 - 3 / 10) / 10);
});
test('testAnd', () {
test('testAnd', () async {
var snippet = "true and false";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testOr', () {
test('testOr', () async {
var snippet = "true or false";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testEqualsTrue', () {
test('testEqualsTrue', () async {
var snippet = "1 = '1'";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testEqualsFalse', () {
test('testEqualsFalse', () async {
var snippet = "1 = 2";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testOrValue', () {
test('testOrValue', () async {
var snippet = "10 or 0";
var expr = parse(snippet);
var result = interpretAsDouble(expr);
var result = await interpretAsDouble(expr);
expect(result, 10);
});
test('testSimpleCondition', () {
test('testSimpleCondition', () async {
var snippet = "true ? 'yes' : 'no'";
var expr = parse(snippet);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "yes");
});
test('testMultiCondition', () {
var snippet = "true ? false and true ? -1 : false or true ? 'yes' : 'no' : -1";
test('testMultiCondition', () async {
var snippet =
"true ? false and true ? -1 : false or true ? 'yes' : 'no' : -1";
var expr = parse(snippet);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "yes");
});
test('testConditionEquals', () {
test('testConditionEquals', () async {
var snippet = "true = false";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testConditionNotEquals', () {
test('testConditionNotEquals', () async {
var snippet = "true != false";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testConditionGreaterThan', () {
test('testConditionGreaterThan', () async {
var snippet = "5 > 10";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testConditionGreaterThanOrEqual', () {
test('testConditionGreaterThanOrEqual', () async {
var snippet = "5 >= 5";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testConditionLessThan', () {
test('testConditionLessThan', () async {
var snippet = "5 < 10";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testConditionLessThanOrEqual', () {
test('testConditionLessThanOrEqual', () async {
var snippet = "5 <= 5";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, true);
});
test('testMinusPlusModifier', () {
test('testMinusPlusModifier', () async {
var snippet = "-5 + -(5+10) - +'5'";
var expr = parse(snippet);
var result = interpretAsDouble(expr);
var result = await interpretAsDouble(expr);
expect(result, -25);
});
test('testNegation', () {
test('testNegation', () async {
var snippet = "!true";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testStringEscaping', () {
test('testStringEscaping', () async {
var snippet = "'asdadsasd\\'asdasd'";
var expr = parse(snippet);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "asdadsasd'asdasd");
});
test('testTypeConversionToNumber', () {
test('testTypeConversionToNumber', () async {
var snippet = "5 + '10.34' + true";
var expr = parse(snippet);
var result = interpretAsDouble(expr);
var result = await interpretAsDouble(expr);
expect(result, 16.34);
});
test('testNanStringToInt', () {
test('testNanStringToInt', () async {
var snippet = "+'asdasd'";
var expr = parse(snippet);
var result = interpretAsDouble(expr);
var result = await interpretAsDouble(expr);
expect(result == null, true);
});
test('testTypeConversionToBool', () {
test('testTypeConversionToBool', () async {
var snippet = "0 ? -1 : 1 ? '' ? -1 : 'yes' : -1";
var expr = parse(snippet);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "yes");
});
test('testTypeConversionStringToBool', () {
test('testTypeConversionStringToBool', () async {
var snippet = "''";
var expr = parse(snippet);
var result = interpretAsBool(expr);
var result = await interpretAsBool(expr);
expect(result, false);
});
test('testStringModeStartWithString', () {
test('testStringModeStartWithString', () async {
var snippet = "Hello {fetchName()}!";
var expr = parse(snippet, true);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "Hello Memri!");
});
test('testStringModeStartWithExpression', () {
test('testStringModeStartWithExpression', () async {
var snippet = "{fetchName()} Hello";
var expr = parse(snippet, true);
var result = interpretAsString(expr);
var result = await interpretAsString(expr);
expect(result, "Memri Hello");
});
}
\ No newline at end of file
}
import 'package:flutter_test/flutter_test.dart';
import 'package:memri/MemriApp/CVU/definitions/CVUValue_Constant.dart';
import 'package:memri/MemriApp/CVU/definitions/CVUValue_Expression.dart';
import 'package:memri/MemriApp/CVU/definitions/CVUValue_LookupNode.dart';
import 'package:memri/MemriApp/CVU/parsing/CVUExpressionLexer.dart';
import 'package:memri/MemriApp/CVU/parsing/CVUExpressionParser.dart';
import 'package:test/test.dart';
parse({snippet, stringMode = false}) {
var lexer = CVUExpressionLexer(snippet, stringMode);
......@@ -366,7 +366,7 @@ void main() {
test('testExample', () {
var snippet =
"""!(test + -5.63537) or 4/3 ? variable.func() : me.address[primary = true].country ? ((4+5 * 10) + test[10]) : 'asdads\\'asdad' + ''""";
"""!(test + -5.63537) or 4/3 ? variable.func() : me.address[primary = true].country ? ((4+5 * 10) + test[10]) : 'asdads\\'asdad' + ''""";
var result = parse(snippet: snippet);
expect(
result,
......@@ -413,7 +413,7 @@ void main() {
var snippet = "true ? 'yes'";
expect(
() => parse(snippet: snippet), throwsA(predicate((e) => e is CVUExpressionParseErrorsExpectedConditionElse)));
() => parse(snippet: snippet), throwsA(predicate((e) => e is CVUExpressionParseErrorsExpectedConditionElse)));
});
test('testErrorIncompleteBinaryOp', () {
......@@ -427,9 +427,9 @@ void main() {
var snippet = "5 @ 4";
expect(
() => parse(snippet: snippet),
() => parse(snippet: snippet),
throwsA(predicate((e) =>
e is CVUExpressionParseErrorsUnexpectedToken &&
e is CVUExpressionParseErrorsUnexpectedToken &&
e.value is ExprTokenIdentifier &&
(e.value as ExprTokenIdentifier).value == "@")));
});
......@@ -458,9 +458,9 @@ void main() {
var snippet = "Hello {fetchName()}";
expect(
() => parse(snippet: snippet),
() => parse(snippet: snippet),
throwsA(predicate((e) =>
e is CVUExpressionParseErrorsUnexpectedToken &&
e is CVUExpressionParseErrorsUnexpectedToken &&
e.value is ExprTokenCurlyBracketOpen &&
(e.value as ExprTokenCurlyBracketOpen).i == 6)));
});
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment