diff --git a/CHANGELOG.md b/CHANGELOG.md index d3dbb9eb..fde302d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 1.9.2 + +- `FieldsFromMap`: + - `resolveFiledName`: improve field matching. + +- `EntityHandler` + - `getFieldType`: added parameter `resolveFiledName: true`. + - Optimize field and types resolution. + +- `GenericEntityHandler`, `ClassReflectionEntityHandler`: + - Optimize field and types resolution. + ## 1.9.1 - async_events: ^1.3.0 diff --git a/lib/src/bones_api_base.dart b/lib/src/bones_api_base.dart index 832d1e25..3e941522 100644 --- a/lib/src/bones_api_base.dart +++ b/lib/src/bones_api_base.dart @@ -42,7 +42,7 @@ typedef APILogger = void Function(APIRoot apiRoot, String type, String? message, /// Bones API Library class. class BonesAPI { // ignore: constant_identifier_names - static const String VERSION = '1.9.1'; + static const String VERSION = '1.9.2'; static bool _boot = false; diff --git a/lib/src/bones_api_condition.dart b/lib/src/bones_api_condition.dart index 5e1d5a62..784d6734 100644 --- a/lib/src/bones_api_condition.dart +++ b/lib/src/bones_api_condition.dart @@ -1240,7 +1240,8 @@ abstract class KeyCondition extends Condition { TypeInfo? _resolveValueType(String? keyName, TypeInfo? objType, EntityHandler? objEntityHandler, Object? value) { if (keyName != null) { - var type = objEntityHandler?.getFieldType(null, keyName); + var type = + objEntityHandler?.getFieldType(null, keyName, resolveFiledName: true); if (type != null) return type; } @@ -1257,8 +1258,8 @@ abstract class KeyCondition extends Condition { return objType ?? TypeInfo.from(value); } - var t = objEntityHandler?.getEntityHandler(obj: value)?.type; - return t != null ? TypeInfo.fromType(t) : null; + var t = objEntityHandler?.getEntityHandler(obj: value)?.typeInfo; + return t; } } diff --git a/lib/src/bones_api_entity.dart b/lib/src/bones_api_entity.dart index 76be1220..897d205a 100644 --- a/lib/src/bones_api_entity.dart +++ b/lib/src/bones_api_entity.dart @@ -535,7 +535,8 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { Type? _idType; - Type idType([O? o]) => _idType ??= getFieldType(o, idFieldName(o))!.type; + Type idType([O? o]) => _idType ??= + getFieldType(o, idFieldName(o), resolveFiledName: false)!.type; String idFieldName([O? o]); @@ -741,11 +742,12 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { } FutureOr resolveEntityFieldValue(O o, String key, dynamic value, - {EntityProvider? entityProvider, + {bool resolveFiledName = false, + EntityProvider? entityProvider, EntityCache? entityCache, EntityResolutionRules? resolutionRules}) { entityCache ??= JsonEntityCacheSimple(); - var fieldType = getFieldType(o, key); + var fieldType = getFieldType(o, key, resolveFiledName: resolveFiledName); return resolveValueByType(fieldType, value, entityProvider: entityProvider, entityCache: entityCache, @@ -1362,39 +1364,49 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { return _fieldsNamesSimple!; } - TypeInfo? getFieldType(O? o, String key); + TypeInfo? getFieldType(O? o, String key, {bool resolveFiledName = false}); + + Map? _fieldsTypes; Map getFieldsTypes([O? o]) { - return Map.fromEntries(fieldsNames(o) - .map((key) => MapEntry(key, getFieldType(o, key)!))); + return _fieldsTypes ??= Map.unmodifiable( + Map.fromEntries( + fieldsNames(o).map( + (key) => MapEntry( + key, + getFieldType(o, key, resolveFiledName: false)!, + ), + ), + ), + ); } Map? _fieldsEnumTypes; Map getFieldsEnumTypes([O? o]) { var enumFields = _fieldsEnumTypes; - if (enumFields != null) return UnmodifiableMapView(enumFields); + if (enumFields != null) return enumFields; final reflectionFactory = ReflectionFactory(); - enumFields = getFieldsTypes(o) + var mapEnumFields = getFieldsTypes(o) .entries .where((e) => reflectionFactory.getRegisterEnumReflection(e.value.type) != null) .toMapFromEntries(); - return UnmodifiableMapView(_fieldsEnumTypes = enumFields); + return _fieldsEnumTypes = Map.unmodifiable(mapEnumFields); } Map? _fieldsEntityTypes; Map getFieldsEntityTypes([O? o]) { var entityFields = _fieldsEntityTypes; - if (entityFields != null) return UnmodifiableMapView(entityFields); + if (entityFields != null) return entityFields; var enumFields = getFieldsEnumTypes(o); - entityFields = getFieldsTypes() + var mapEntityFields = getFieldsTypes() .entries .map((e) { var field = e.key; @@ -1412,7 +1424,7 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { .nonNulls .toMapFromEntries(); - return UnmodifiableMapView(_fieldsEntityTypes = entityFields); + return _fieldsEntityTypes = Map.unmodifiable(mapEntityFields); } List? getFieldEntityAnnotations(O? o, String key); @@ -1495,7 +1507,7 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { } if (resolvedValue != null) { - var fieldType = getFieldType(o, key); + var fieldType = getFieldType(o, key, resolveFiledName: true); if (fieldType != null) { EntityHandler? fieldEntityHandler; @@ -2146,6 +2158,7 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { var setFutures = fieldsValues.entries.map((e) { return setFieldValueDynamic(o, e.key, e.value, + resolveFiledName: false, entityProvider: entityProvider, entityCache: entityCache, resolutionRules: resolutionRules) @@ -2156,10 +2169,12 @@ abstract class EntityHandler with FieldsFromMap, EntityRulesResolver { } FutureOr setFieldValueDynamic(O o, String key, dynamic value, - {EntityProvider? entityProvider, + {bool resolveFiledName = false, + EntityProvider? entityProvider, EntityCache? entityCache, EntityResolutionRules? resolutionRules}) { var retValue2 = resolveEntityFieldValue(o, key, value, + resolveFiledName: resolveFiledName, entityProvider: entityProvider, entityCache: entityCache, resolutionRules: resolutionRules); @@ -2312,8 +2327,6 @@ class GenericEntityHandler extends EntityHandler { return fieldsNames; } - Map? _fieldsTypes; - @override Map fieldsTypes([O? o]) { var fieldsTypes = _fieldsTypes; @@ -2344,19 +2357,28 @@ class GenericEntityHandler extends EntityHandler { if (o != null && _idFieldsName == null) { _idFieldsName = o.idFieldName; - _fieldsNames ??= List.unmodifiable(o.fieldsNames); + final fieldsNames = + _fieldsNames ??= List.unmodifiable(o.fieldsNames); _fieldsTypes ??= Map.unmodifiable( - Map.fromEntries( - _fieldsNames!.map((f) => MapEntry(f, o!.getFieldType(f)!)))); + Map.fromEntries( + fieldsNames.map((f) => MapEntry(f, o!.getFieldType(f)!)), + ), + ); _fieldsEntityAnnotations ??= Map>.unmodifiable( - Map>.fromEntries( - _fieldsNames!.map((f) { - var list = o!.getFieldEntityAnnotations(f); - return list == null ? null : MapEntry(f, UnmodifiableListView(list)); - }).nonNulls)); + Map>.fromEntries( + fieldsNames.map( + (f) { + var list = o!.getFieldEntityAnnotations(f); + return list == null + ? null + : MapEntry(f, List.unmodifiable(list)); + }, + ).nonNulls, + ), + ); } } @@ -2409,13 +2431,33 @@ class GenericEntityHandler extends EntityHandler { } @override - TypeInfo? getFieldType(O? o, String key) { + TypeInfo? getFieldType(O? o, String key, {bool resolveFiledName = false}) { inspectObject(o); - if (o != null) { - return o.getFieldType(key); - } else { - return _fieldsTypes?[key]; + + TypeInfo? fieldType; + + final fieldsTypes = _fieldsTypes; + if (fieldsTypes != null) { + fieldType = fieldsTypes[key]; + + if (fieldType == null && resolveFiledName) { + var resolvedFieldName = this.resolveFiledName(fieldsNames(), key); + if (resolvedFieldName != null) { + fieldType = fieldsTypes[resolvedFieldName]; + } + } + } else if (o != null) { + fieldType = o.getFieldType(key); + + if (fieldType == null && resolveFiledName) { + var resolvedFieldName = this.resolveFiledName(fieldsNames(), key); + if (resolvedFieldName != null) { + fieldType = o.getFieldType(key); + } + } } + + return fieldType; } @override @@ -2535,9 +2577,35 @@ class ClassReflectionEntityHandler extends EntityHandler { reflection.getJsonFieldsVisibleValues(o); @override - TypeInfo? getFieldType(O? o, String key) { - var field = reflection.field(key, o); - return field != null ? TypeInfo.from(field) : null; + TypeInfo? getFieldType(O? o, String key, {bool resolveFiledName = false}) { + TypeInfo? fieldType; + + final fieldsTypes = _fieldsTypes; + if (fieldsTypes != null) { + fieldType = fieldsTypes[key]; + + if (fieldType == null && resolveFiledName) { + var resolvedFieldName = this.resolveFiledName(fieldsNames(), key); + if (resolvedFieldName != null) { + fieldType = fieldsTypes[resolvedFieldName]; + } + } + } else { + var field = reflection.field(key, o); + + if (field == null && resolveFiledName) { + var resolvedFieldName = this.resolveFiledName(fieldsNames(), key); + if (resolvedFieldName != null) { + field = reflection.field(resolvedFieldName, o); + } + } + + if (field != null) { + fieldType = TypeInfo.from(field); + } + } + + return fieldType; } List? getFieldAnnotations(O? o, String key) { @@ -2686,45 +2754,37 @@ class ClassReflectionEntityHandler extends EntityHandler { List fieldsNames([O? o]) => _fieldsNames ??= List.unmodifiable(fieldsTypes(o).keys); - Map? _fieldsTypes; - @override Map fieldsTypes([O? o]) { - if (_fieldsTypes == null) { - var reflection = reflectionWithObject(o); - - var types = reflection.fieldsNames.map((f) { - var field = reflection.field(f, o); - if (field == null || !field.hasSetter) return null; - var type = TypeInfo.from(field); - return MapEntry(f, type); - }); - - _fieldsTypes = UnmodifiableMapView( - Map.fromEntries(types.nonNulls)); - } - return _fieldsTypes!; + return _fieldsTypes ??= Map.unmodifiable( + Map.fromEntries( + reflectionWithObject(o).fieldsNames.map( + (f) { + var field = reflection.field(f, o); + if (field == null || !field.hasSetter) return null; + var type = TypeInfo.from(field); + return MapEntry(f, type); + }, + ).nonNulls, + ), + ); } Map>? _constructors; @override Map>? constructors([O? o]) { - if (_constructors == null) { - var reflection = reflectionWithObject(o); - - var map = reflection.allConstructors().map((c) { - var name = c.name; - var args = c.allParameters - .map((p) => MapEntry(p.jsonName, p.type.typeInfo)) - .toMapFromEntries(); - return MapEntry(name, UnmodifiableMapView(args)); - }).toMapFromEntries(); - - _constructors = UnmodifiableMapView(map); - } - - return _constructors!; + return _constructors ??= Map.unmodifiable( + reflectionWithObject(o).allConstructors().map( + (c) { + var name = c.name; + var args = c.allParameters + .map((p) => MapEntry(p.jsonName, p.type.typeInfo)) + .toMapFromEntries(); + return MapEntry(name, Map.unmodifiable(args)); + }, + ).toMapFromEntries(), + ); } @override @@ -6479,7 +6539,7 @@ abstract class IterableEntityRepository {TypeInfo? fieldType, Transaction? transaction}) { checkNotClosed(); - fieldType ??= entityHandler.getFieldType(o, field)!; + fieldType ??= entityHandler.getFieldType(o, field, resolveFiledName: true)!; if (!fieldType.isListEntityOrReference) { throw StateError("Field `$field` not a `List` entity type: $fieldType"); @@ -6512,7 +6572,7 @@ abstract class IterableEntityRepository {Object? oId, TypeInfo? fieldType, Transaction? transaction}) { checkNotClosed(); - fieldType ??= entityHandler.getFieldType(o, field)!; + fieldType ??= entityHandler.getFieldType(o, field, resolveFiledName: true)!; if (!fieldType.isListEntityOrReference) { throw StateError("Field `$field` not a `List` entity type: $fieldType"); @@ -6607,7 +6667,8 @@ abstract class IterableEntityRepository var value = entityHandler.getField(o, fieldName); if (value == null) return null; - var fieldType = entityHandler.getFieldType(o, fieldName)!; + var fieldType = + entityHandler.getFieldType(o, fieldName, resolveFiledName: false)!; if (!EntityHandler.isValidEntityType(fieldType.type)) { return null; diff --git a/lib/src/bones_api_entity_db.dart b/lib/src/bones_api_entity_db.dart index 877eace0..a9be5cbe 100644 --- a/lib/src/bones_api_entity_db.dart +++ b/lib/src/bones_api_entity_db.dart @@ -429,7 +429,8 @@ abstract class DBAdapter extends SchemeProvider entityName: entityName, tableName: tableName, entityType: entityType); if (entityRepository != null) { - var type = entityRepository.entityHandler.getFieldType(null, field); + var type = entityRepository.entityHandler + .getFieldType(null, field, resolveFiledName: true); return type; } diff --git a/lib/src/bones_api_entity_db_memory.dart b/lib/src/bones_api_entity_db_memory.dart index 9810f827..e92e8e9b 100644 --- a/lib/src/bones_api_entity_db_memory.dart +++ b/lib/src/bones_api_entity_db_memory.dart @@ -561,7 +561,8 @@ class DBSQLMemoryAdapter extends DBSQLAdapter return MapEntry(key, value); } - var fieldType = entityHandler.getFieldType(null, key); + var fieldType = + entityHandler.getFieldType(null, key, resolveFiledName: false); if (fieldType != null) { if (fieldType.isEntityReferenceType && value is EntityReference) { @@ -1157,8 +1158,9 @@ class DBSQLMemoryAdapter extends DBSQLAdapter if (targetEntityHandler != null) { targetName = getEntityRepositoryByType(targetEntityHandler.type)?.name; targetIdField = targetEntityHandler.idFieldName(); - targetIdType = - targetEntityHandler.getFieldType(null, targetIdField)?.type; + targetIdType = targetEntityHandler + .getFieldType(null, targetIdField, resolveFiledName: false) + ?.type; } targetName ??= targetEntityType.type.toString().toLowerCase(); @@ -1228,8 +1230,10 @@ class DBSQLMemoryAdapter extends DBSQLAdapter var sourceEntityHandler = repo.entityHandler; var sourceFieldId = sourceEntityHandler.idFieldName(); - var sourceFieldIdType = - sourceEntityHandler.getFieldType(null, sourceFieldId)?.type ?? int; + var sourceFieldIdType = sourceEntityHandler + .getFieldType(null, sourceFieldId, resolveFiledName: false) + ?.type ?? + int; var relTable = '${sourceTable}__${sourceField}__rel'; var relSourceField = '${sourceTable}__$sourceFieldId'; diff --git a/lib/src/bones_api_entity_db_object_directory.dart b/lib/src/bones_api_entity_db_object_directory.dart index 3aa0cce6..a9393864 100644 --- a/lib/src/bones_api_entity_db_object_directory.dart +++ b/lib/src/bones_api_entity_db_object_directory.dart @@ -638,7 +638,8 @@ class DBObjectDirectoryAdapter return MapEntry(key, dataURLBase64.toString()); } - var fieldType = entityHandler.getFieldType(null, key); + var fieldType = + entityHandler.getFieldType(null, key, resolveFiledName: false); if (fieldType != null) { if (fieldType.isEntityReferenceType) { diff --git a/lib/src/bones_api_entity_db_object_gcs.dart b/lib/src/bones_api_entity_db_object_gcs.dart index 2177a2c9..8785f1e5 100644 --- a/lib/src/bones_api_entity_db_object_gcs.dart +++ b/lib/src/bones_api_entity_db_object_gcs.dart @@ -897,7 +897,8 @@ class DBObjectGCSAdapter extends DBObjectAdapter { return MapEntry(key, dataURLBase64.toString()); } - var fieldType = entityHandler.getFieldType(null, key); + var fieldType = + entityHandler.getFieldType(null, key, resolveFiledName: false); if (fieldType != null) { if (fieldType.isEntityReferenceType) { diff --git a/lib/src/bones_api_entity_db_object_memory.dart b/lib/src/bones_api_entity_db_object_memory.dart index c7d38564..f1b40d93 100644 --- a/lib/src/bones_api_entity_db_object_memory.dart +++ b/lib/src/bones_api_entity_db_object_memory.dart @@ -647,7 +647,8 @@ class DBObjectMemoryAdapter return MapEntry(key, value); } - var fieldType = entityHandler.getFieldType(null, key); + var fieldType = + entityHandler.getFieldType(null, key, resolveFiledName: false); if (fieldType != null) { if (fieldType.isEntityReferenceType) { diff --git a/lib/src/bones_api_entity_db_relational.dart b/lib/src/bones_api_entity_db_relational.dart index 6f4f1cf2..2d01f226 100644 --- a/lib/src/bones_api_entity_db_relational.dart +++ b/lib/src/bones_api_entity_db_relational.dart @@ -278,7 +278,8 @@ class DBRelationalEntityRepository _nonPrimitiveFields ??= entityHandler .fieldsNames(o) .map((fieldName) { - var fieldType = entityHandler.getFieldType(o, fieldName)!; + var fieldType = entityHandler.getFieldType(o, fieldName, + resolveFiledName: false)!; if (fieldType.isPrimitiveType) return null; return MapEntry(fieldName, fieldType); }) @@ -574,7 +575,7 @@ class DBRelationalEntityRepository FutureOr setRelationship( O o, String field, List values, {TypeInfo? fieldType, Transaction? transaction}) { - fieldType ??= entityHandler.getFieldType(o, field)!; + fieldType ??= entityHandler.getFieldType(o, field, resolveFiledName: true)!; var op = TransactionOperationStoreRelationship( name, operationExecutor, o, values, @@ -610,7 +611,7 @@ class DBRelationalEntityRepository @override FutureOr> selectRelationship(O? o, String field, {Object? oId, TypeInfo? fieldType, Transaction? transaction}) { - fieldType ??= entityHandler.getFieldType(o, field)!; + fieldType ??= entityHandler.getFieldType(o, field, resolveFiledName: true)!; oId ??= entityHandler.getID(o!); @@ -747,7 +748,8 @@ class DBRelationalEntityRepository .map((o) => getID(o, entityHandler: entityHandler)! as Object) .toList(); - fieldType ??= entityHandler.getFieldType(os?.first, field)!; + fieldType ??= + entityHandler.getFieldType(os?.first, field, resolveFiledName: true)!; if (oIds.isEmpty) { return >{}; diff --git a/lib/src/bones_api_entity_db_sql.dart b/lib/src/bones_api_entity_db_sql.dart index 8db454b5..b7c3951d 100644 --- a/lib/src/bones_api_entity_db_sql.dart +++ b/lib/src/bones_api_entity_db_sql.dart @@ -644,7 +644,8 @@ abstract class DBSQLAdapter extends DBRelationalAdapter for (var e in schemesTypes.entries) { var field = e.key; var schemeType = e.value; - var fieldType = entityHandler.getFieldType(null, field); + var fieldType = + entityHandler.getFieldType(null, field, resolveFiledName: true); if (!checkDBTableField(repoType, field, schemeType, fieldType)) { return _DBTableCheck.error( @@ -757,8 +758,23 @@ abstract class DBSQLAdapter extends DBRelationalAdapter missingEnumConstraints = entityEnumFields.whereNotIn(enumConstraintsFields).toSet(); + var entityFieldsNamesIndexes = entityHandler.fieldsNamesIndexes(); + var entityFieldsNames = entityHandler.fieldsNames(); + var entityFieldsNamesLC = entityHandler.fieldsNamesLC(); + var entityFieldsNamesSimple = entityHandler.fieldsNamesSimple(); + for (var e in enumConstraints) { - var fieldType = entityHandler.getFieldType(null, e.field); + var fieldName = entityHandler.resolveFiledName( + entityFieldsNames, + e.field, + fieldsNamesIndexes: entityFieldsNamesIndexes, + fieldsNamesLC: entityFieldsNamesLC, + fieldsNamesSimple: entityFieldsNamesSimple, + ) ?? + e.field; + + var fieldType = entityHandler.getFieldType(null, fieldName, + resolveFiledName: false); if (fieldType == null) continue; var enumReflection = @@ -828,7 +844,8 @@ abstract class DBSQLAdapter extends DBRelationalAdapter String table, String fieldName, ) { - var fieldType = entityHandler.getFieldType(null, fieldName); + var fieldType = + entityHandler.getFieldType(null, fieldName, resolveFiledName: false); if (fieldType == null) return null; var entityType = fieldType.entityTypeInfo ?? fieldType.listEntityType; if (entityType == null) return null; @@ -2732,7 +2749,10 @@ class _DBTableColumn { factory _DBTableColumn.fromEntityHandler( EntityHandler entityHandler, String fieldName) { - var type = entityHandler.getFieldType(null, fieldName) ?? TypeInfo.tString; + var type = + entityHandler.getFieldType(null, fieldName, resolveFiledName: true) ?? + TypeInfo.tString; + var annotations = entityHandler .getFieldEntityAnnotations(null, fieldName) ?.whereType() diff --git a/lib/src/bones_api_mixin.dart b/lib/src/bones_api_mixin.dart index 9ee2276b..879e9db7 100644 --- a/lib/src/bones_api_mixin.dart +++ b/lib/src/bones_api_mixin.dart @@ -542,9 +542,13 @@ mixin FieldsFromMap { bool includeAbsentFields = false, List? returnMapUsedKeys, }) { - var f = fieldsNames.firstWhereOrNull((f) => f == fieldName); + var fieldResolved = fieldsNames.firstWhereOrNull((f) => f == fieldName); + if (fieldResolved != null) return fieldResolved; - f ??= fieldsNames.firstWhereOrNull((f) { + String? fieldNameLC; + String? fieldNameSimple; + + fieldResolved = fieldsNames.firstWhereOrNull((f) { String? fLC, fSimple; if (fieldsNamesIndexes != null) { var idx = fieldsNamesIndexes[f]!; @@ -552,16 +556,18 @@ mixin FieldsFromMap { fSimple = fieldsNamesSimple?[idx]; } + fieldNameLC ??= fieldToLCKey(fieldName); fLC ??= fieldToLCKey(f); - if (fieldName == fLC) return true; + if (fieldNameLC == fLC) return true; + fieldNameSimple ??= fieldToSimpleKey(fieldName); fSimple ??= fieldToSimpleKey(f); - if (fieldName == fSimple) return true; + if (fieldNameSimple == fSimple) return true; return false; }); - return f; + return fieldResolved; } /// Returns a [Map] with the fields values populated from the provided [map]. diff --git a/lib/src/bones_api_sql_builder.dart b/lib/src/bones_api_sql_builder.dart index bb923626..de457aa0 100644 --- a/lib/src/bones_api_sql_builder.dart +++ b/lib/src/bones_api_sql_builder.dart @@ -1510,7 +1510,8 @@ abstract mixin class SQLGenerator { entityFieldAnnotations: entityFieldAnnotations); if (enumType == null) { - throw StateError("Can't find `EnumReflection` for type: $fieldType"); + throw StateError( + "Can't find column `$table`.`$columnName` `EnumReflection` for type: $fieldType"); } var type = enumType.key; diff --git a/pubspec.yaml b/pubspec.yaml index 633695f6..8a3a57f0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bones_api description: Bones_API - A powerful API backend framework for Dart. It comes with a built-in HTTP Server, route handler, entity handler, SQL translator, and DB adapters. -version: 1.9.1 +version: 1.9.2 homepage: https://github.com/Colossus-Services/bones_api environment: