Get interface from Enum extension that has been casted as dynamic[dart] - flutter

I'm wondering if there is a way to get the camelcase function interface where 'user.role' in the second batch of code is an Enum.
enum Role {
influencer,
brand,
agency,
}
extension EnumEx on Enum {
String get name {
//convert to lowercase and capitalize
var x = toString().split('.').last;
return '${x[0].toUpperCase()}${x.substring(1).toLowerCase()}';
}
//String get camelcase => toString().toUpperCase().replaceAll('.', '_');
String camelcase() {
var x = toString();
return x.toUpperCase().replaceAll('.', '_');
}
}
........
return FirebaseFirestore.instance.collection('user').add(<String, dynamic>{
'owner_id': FirebaseAuth.instance.currentUser!.uid,
'agency_id': user.agencyId,
'profile_photo_url': user.profilePhotoUrl,
'banner_photo_url': user.bannerPhotoUrl,
'bio': user.bio,
'user_role': user.role,

Related

Dart Factory class for creating variables with type

The problem is the following.
I had a typescript factory class that I attempted to do in Dart:
class FactoryClass{
factory FactoryClass(dynamic types, String className, dynamic defaultValue){
if(types[className] != null ){
return types[className](defaultValue);
}
else{
throw Exception("");
}
}
}
In TS it was used like this:
let variable= new FactoryClass([String, Number, etc...], "Number", "42")
That in TypeScript would give back a Number type variable with the value 42
However, it's not gonna work in Dart since types have no constructor for this. So I can't do something like
final myString = new String("def_value")
So the question arises, how can I go about it in dart?
You can do similar in Dart with just functions:
typedef Factory = dynamic Function(dynamic value);
dynamic create(Map<String, Factory> types, String className, dynamic defaultValue) {
if (types.containsKey(className)) {
return types[className]!(defaultValue);
} else {
throw Exception("no factory for $className");
}
}
final factories = <String, Factory>{
'String': (s) => s.toString(),
'int': (i) => i is int ? i : int.parse('$i'),
'bool': (b) => b is bool ? b : ('$b' == 'true'),
};
show(v) => print('Value $v has type ${v.runtimeType}');
main() {
show(create(factories, 'String', 'foo'));
show(create(factories, 'int', '42'));
show(create(factories, 'bool', 'false'));
}
Prints:
Value foo has type String
Value 42 has type int
Value false has type bool

How to update Model with the correct value type

I have this code that fetch data from internet:
Future fetchCfo(String barCode, String url) async {
final response = await http.get(Uri.parse(url + barCode));
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(utf8.decode(response.bodyBytes));
responseBody.forEach((key, value) {
if (value == null) {
responseBody.update(key, (value) => "");
}
});
return CfoModel.fromJson(responseBody);
} else {
return null;
}
}
And I need to replace the null values ​​according to their type, for example if a field that was supposed to be an integer returns a null I need to replace it with 0, if a field that was supposed to be a list returns a null I need to replace it with a list empty []; and I don't know how to do this, my code replaces Strings but if a null appears in an int field, the code breaks.
For example, my model:
String? interfaceStatus;
bool? trasnP;
int? id;
String? name;
String? date;
bool? requiredStatus;
int? numberOf;
int? numberRegion;
List? parameters;
In case numberRegion returns a null, I need to replace with 0;
In case Transp returns a null, I need to replace with false;
in case name returns a null, I need to replace with empty String "";
Response I'm getting:
{
"interfaceStatus":"ok",
"trasnP":false,
"id":null,
"name":null,
"date":null,
"requiredStatus":true,
"numberOf":23,
"numberRegion":27,
"parameters":null
}
What I want to return:
{
"interfaceStatus":"ok",
"trasnP":false,
"id":0,
"name":"",
"date":"",
"requiredStatus":true,
"numberOf":23,
"numberRegion":27,
"Parameters":[
]
}
Anyone has a solution for this ?
I would recommend not updating any values in the responseBody just leave them as null but in the fromJson function you can give default values with the ?? operator such as:
fromJson(Map response) {
return CfoModel(
id: response[id] ?? 0,
array: response[array_element] ?? [],
);
}

How to compare the type variable in "is" operator in Dart

I couldn't find a way to store the Type value in Map so that I could use it in is operator to check the validity of type using this map later on. Also, can is operator accept Type as a variable?
For eg, Below is hypothetical code solving the problem but it's invalid.
Map<String, Type> map = {
"sku": String,
"price": double,
"quantity": int,
};
dynamic value = 10;
if(value is map["quantity"]){
print("value is of type int and int is expected for quantity value");
}
You can do something like this:
class TypeCheck<T> {
const TypeCheck();
bool typeCheck(dynamic value) => value is T;
}
void main() {
Map<String, TypeCheck> map = {
"sku": TypeCheck<String>(),
"price": TypeCheck<double>(),
"quantity": TypeCheck<int>(),
};
dynamic value = 10;
if (map["quantity"]!.typeCheck(value)) {
print("value is of type int and int is expected for quantity value");
}
}
Im not sure I fully understand I understand what you are trying to do but why don't you try something like.
bool _validate(Map productDetails){
if (productDetails.containsKey("sold_individually") && productDetails["sold_individually"] is bool) {
//return true or false
}
else if (productDetails.containsKey("stock_quantity") && productDetails["stock_quantity"] is int){
//return true or false
}
else if (productDetails.containsKey("tax_class") && productDetails["tax_class"] is String && productDetails["tax_class"].isNotEmpty) {
//return true or false
} else {
//return true or false
}
}
As for the other part of your question you wont get an error but you will always return false. In contrast if you check if a variable is dynamic it will always return true.
I don't really understand your end goal. But from what you have, I don't think you are taking advantage of the strongly-typed nature of dart.
Assuming you are getting your map from an API, you could enforce
typing manually in your code as follows;
Map<String, Type> map = {
"sku": json['key'] as String,
"price": json['key'] as double,
"quantity": json['key'] as int,
};
And avoid using dynamic when declaring variables.
OR
In the case you have a user-defined type you what to compare, you can use the equatable package on a class for instance as follows;
class CustomMap extends Equatable {
String sky;
double price;
int quantity;
// here you put the fields of a class you want for two instances of a class to be equal.
#overide
List<Object> get props => [sky, price, quantity];
}
Update from your comment
You should have a custom class for the API objects for instance;
class Item extends Equatable {
String sku;
double price;
int quantity;
Item({this.sky, this.price, this.quantity});
// factory constructor
factory Item.fromMap(Map<String, dynmic> json) {
final sku = json['sku'] as String,
final price = (json['price'] as num) as double,
final quantity = json['quantity'] as num,
return Item(sku: sku, price: price, quantity: quantity);
}
// define equatable objects
#override
List<Object> get props => [sku, price, quantity];
}
Now you can use it as follows;
Future<Item> objectsFromService(Map<String, dynamic> json ) async {
http.Response response = http.get(url);
if(response.status == 200) {
final decodedJson = json.decode(response.body);
return Item.fromJson(decodedJson);
}else{
print('Error fetch data');
return null;
}
}
Hope it helps

How to construct a dynamic where filter in EF.Core to handle equals, LIKE, gt, lt, etc

Please how do we construct a dynamic where filter in EF.Core to handle:
Query.Where(fieldName, compareMode, value)
I basically Expect to use it like below:
[HttpGet(Name = nameof(GetStaff))]
public IActionResult GetStaffAsync([FromQuery] QueryParams p)
{
var s = db.Staff.AsNoTracking()
.Where(p.filter_field, p.filter_mode, p.filter_value)
.OrderByMember(p.sortBy, p.descending);
var l = new Pager<Staff>(s, p.page, p.rowsPerPage);
return Ok(l);
}
//Helpers
public class QueryParams
{
public bool descending { get; set; }
public int page { get; set; } = 1;
public int rowsPerPage { get; set; } = 5;
public string sortBy { get; set; }
public onject filter_value { get; set; }
public string filter_field { get; set; }
public string filter_mode { get; set; }
}
public class Pager<T>
{
public int pages { get; set; }
public int total { get; set; }
public IEnumerable<T> Items { get; set; }
public Pager(IEnumerable<T> items, int offset, int limit)
{
Items = items.Skip((offset - 1) * limit).Take(limit).ToList<T>();
total = items.Count();
pages = (int)Math.Ceiling((double)total / limit);
}
}
Assuming all you have is the entity type and strings representing the property, comparison operator and the value, building dynamic predicate can be done with something like this:
public static partial class ExpressionUtils
{
public static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, string comparison, string value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.Property);
var body = MakeComparison(left, comparison, value);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
private static Expression MakeComparison(Expression left, string comparison, string value)
{
switch (comparison)
{
case "==":
return MakeBinary(ExpressionType.Equal, left, value);
case "!=":
return MakeBinary(ExpressionType.NotEqual, left, value);
case ">":
return MakeBinary(ExpressionType.GreaterThan, left, value);
case ">=":
return MakeBinary(ExpressionType.GreaterThanOrEqual, left, value);
case "<":
return MakeBinary(ExpressionType.LessThan, left, value);
case "<=":
return MakeBinary(ExpressionType.LessThanOrEqual, left, value);
case "Contains":
case "StartsWith":
case "EndsWith":
return Expression.Call(MakeString(left), comparison, Type.EmptyTypes, Expression.Constant(value, typeof(string)));
default:
throw new NotSupportedException($"Invalid comparison operator '{comparison}'.");
}
}
private static Expression MakeString(Expression source)
{
return source.Type == typeof(string) ? source : Expression.Call(source, "ToString", Type.EmptyTypes);
}
private static Expression MakeBinary(ExpressionType type, Expression left, string value)
{
object typedValue = value;
if (left.Type != typeof(string))
{
if (string.IsNullOrEmpty(value))
{
typedValue = null;
if (Nullable.GetUnderlyingType(left.Type) == null)
left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type));
}
else
{
var valueType = Nullable.GetUnderlyingType(left.Type) ?? left.Type;
typedValue = valueType.IsEnum ? Enum.Parse(valueType, value) :
valueType == typeof(Guid) ? Guid.Parse(value) :
Convert.ChangeType(value, valueType);
}
}
var right = Expression.Constant(typedValue, left.Type);
return Expression.MakeBinary(type, left, right);
}
}
Basically building property accessor (with nested property support), parsing the comparison operator and calling the corresponding operator/method, dealing with from/to string and from/to nullable type conversions. It can be extended to handle EF Core specific functions like EF.Functions.Like by adding the corresponding branch.
It can be used directly (in case you need to combine it with other predicates) or via custom extension method like this:
public static partial class QueryableExtensions
{
public static IQueryable<T> Where<T>(this IQueryable<T> source, string propertyName, string comparison, string value)
{
return source.Where(ExpressionUtils.BuildPredicate<T>(propertyName, comparison, value));
}
}
based on Ivans answer this is what i came up with
public static class ExpressionUtils
{
public static Expression<Func<T, bool>> BuildPredicate<T>(string propertyName, string comparison, object value)
{
var parameter = Expression.Parameter(typeof(T));
var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
var body = MakeComparison(left, comparison, value);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
static Expression MakeComparison(Expression left, string comparison, object value)
{
var constant = Expression.Constant(value, left.Type);
switch (comparison)
{
case "==":
return Expression.MakeBinary(ExpressionType.Equal, left, constant);
case "!=":
return Expression.MakeBinary(ExpressionType.NotEqual, left, constant);
case ">":
return Expression.MakeBinary(ExpressionType.GreaterThan, left, constant);
case ">=":
return Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, left, constant);
case "<":
return Expression.MakeBinary(ExpressionType.LessThan, left, constant);
case "<=":
return Expression.MakeBinary(ExpressionType.LessThanOrEqual, left, constant);
case "Contains":
case "StartsWith":
case "EndsWith":
if (value is string)
{
return Expression.Call(left, comparison, Type.EmptyTypes, constant);
}
throw new NotSupportedException($"Comparison operator '{comparison}' only supported on string.");
default:
throw new NotSupportedException($"Invalid comparison operator '{comparison}'.");
}
}
}
and some tests
public class Tests
{
[Fact]
public void Nested()
{
var list = new List<Target>
{
new Target
{
Member = "a"
},
new Target
{
Member = "bb"
}
};
var result = list.AsQueryable()
.Where(ExpressionUtils.BuildPredicate<Target>("Member.Length", "==", 2))
.Single();
Assert.Equal("bb", result.Member);
}
[Fact]
public void Field()
{
var list = new List<TargetWithField>
{
new TargetWithField
{
Field = "Target1"
},
new TargetWithField
{
Field = "Target2"
}
};
var result = list.AsQueryable()
.Where(ExpressionUtils.BuildPredicate<TargetWithField>("Field", "==", "Target2"))
.Single();
Assert.Equal("Target2", result.Field);
}
[Theory]
[InlineData("Name", "==", "Person 1", "Person 1")]
[InlineData("Name", "!=", "Person 2", "Person 1")]
[InlineData("Name", "Contains", "son 2", "Person 2")]
[InlineData("Name", "StartsWith", "Person 2", "Person 2")]
[InlineData("Name", "EndsWith", "son 2", "Person 2")]
[InlineData("Age", "==", 13, "Person 2")]
[InlineData("Age", ">", 12, "Person 2")]
[InlineData("Age", "!=", 12, "Person 2")]
[InlineData("Age", ">=", 13, "Person 2")]
[InlineData("Age", "<", 13, "Person 1")]
[InlineData("Age", "<=", 12, "Person 1")]
public void Combos(string name, string expression, object value, string expectedName)
{
var people = new List<Person>
{
new Person
{
Name = "Person 1",
Age = 12
},
new Person
{
Name = "Person 2",
Age = 13
}
};
var result = people.AsQueryable()
.Where(ExpressionUtils.BuildPredicate<Person>(name, expression, value))
.Single();
Assert.Equal(expectedName, result.Name);
}
}
I modified the answer I found here: Linq WHERE EF.Functions.Like - Why direct properties work and reflection does not?
I chucked together a version for those using NpgSQL as their EF Core provider as you will need to use the ILike function instead if you want case-insensitivity, also added a second version which combines a bunch of properties into a single Where() clause:
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, string searchTerm)
{
// Check property name
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentNullException(nameof(propertyName));
}
// Check the search term
if(string.IsNullOrEmpty(searchTerm))
{
throw new ArgumentNullException(nameof(searchTerm));
}
// Check the property exists
var property = typeof(T).GetProperty(propertyName);
if (property == null)
{
throw new ArgumentException($"The property {typeof(T)}.{propertyName} was not found.", nameof(propertyName));
}
// Check the property type
if(property.PropertyType != typeof(string))
{
throw new ArgumentException($"The specified property must be of type {typeof(string)}.", nameof(propertyName));
}
// Get expression constants
var searchPattern = "%" + searchTerm + "%";
var itemParameter = Expression.Parameter(typeof(T), "item");
var functions = Expression.Property(null, typeof(EF).GetProperty(nameof(EF.Functions)));
var likeFunction = typeof(NpgsqlDbFunctionsExtensions).GetMethod(nameof(NpgsqlDbFunctionsExtensions.ILike), new Type[] { functions.Type, typeof(string), typeof(string) });
// Build the property expression and return it
Expression selectorExpression = Expression.Property(itemParameter, property.Name);
selectorExpression = Expression.Call(null, likeFunction, functions, selectorExpression, Expression.Constant(searchPattern));
return source.Where(Expression.Lambda<Func<T, bool>>(selectorExpression, itemParameter));
}
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, IEnumerable<string> propertyNames, string searchTerm)
{
// Check property name
if (!(propertyNames?.Any() ?? false))
{
throw new ArgumentNullException(nameof(propertyNames));
}
// Check the search term
if (string.IsNullOrEmpty(searchTerm))
{
throw new ArgumentNullException(nameof(searchTerm));
}
// Check the property exists
var properties = propertyNames.Select(p => typeof(T).GetProperty(p)).AsEnumerable();
if (properties.Any(p => p == null))
{
throw new ArgumentException($"One or more specified properties was not found on type {typeof(T)}: {string.Join(",", properties.Where(p => p == null).Select((p, i) => propertyNames.ElementAt(i)))}.", nameof(propertyNames));
}
// Check the property type
if (properties.Any(p => p.PropertyType != typeof(string)))
{
throw new ArgumentException($"The specified properties must be of type {typeof(string)}: {string.Join(",", properties.Where(p => p.PropertyType != typeof(string)).Select(p => p.Name))}.", nameof(propertyNames));
}
// Get the expression constants
var searchPattern = "%" + searchTerm + "%";
var itemParameter = Expression.Parameter(typeof(T), "item");
var functions = Expression.Property(null, typeof(EF).GetProperty(nameof(EF.Functions)));
var likeFunction = typeof(NpgsqlDbFunctionsExtensions).GetMethod(nameof(NpgsqlDbFunctionsExtensions.ILike), new Type[] { functions.Type, typeof(string), typeof(string) });
// Build the expression and return it
Expression selectorExpression = null;
foreach (var property in properties)
{
var previousSelectorExpression = selectorExpression;
selectorExpression = Expression.Property(itemParameter, property.Name);
selectorExpression = Expression.Call(null, likeFunction, functions, selectorExpression, Expression.Constant(searchPattern));
if(previousSelectorExpression != null)
{
selectorExpression = Expression.Or(previousSelectorExpression, selectorExpression);
}
}
return source.Where(Expression.Lambda<Func<T, bool>>(selectorExpression, itemParameter));
}

Variant type and non, with same name

i've tried to use typelite with entity framework and Identity framework.
in identity framework exists various types in the format TypeName<Tkey> and TypeName: TypeName<String>.
typelite correctly exports the types, but this behaviour is not possible in typescript, how should i work around that ?
Class:
namespace DTO
{
[TypeLite.TsClass]
public class Test : Test<String>
{
}
public class Test<TKey>
{
public TKey Id { get; set; }
public String Name { get; set; }
}
}
Configuration 1:
<# var ts = TypeScript.Definitions()
.WithReference("Enums.ts")
.ForLoadedAssemblies();
#>
Output 1, here the error is: Type 'Test<TKey>' recursively references itself as a base type.
declare module DTO {
interface Test extends DTO.Test<string> {
}
interface Test<TKey> {
Id: TKey;
Name: string;
}
}
Configuration 2:
<# var ts = TypeScript.Definitions()
.WithReference("Enums.ts")
.ForLoadedAssemblies()
.WithTypeFormatter((type, f) => "I" + ((TypeLite.TsModels.TsClass)type).Name);
#>
Output 2, here the variant Type got the I too and things got messed up badly
declare module DTO {
interface IUser {
Id: string;
Name: string;
Surname: string;
UserName: string;
Email: string;
Roles: string[];
}
interface ITest extends DTO.ITest {
}
interface ITest {
Id: ITKey;
Name: string;
}
}
If you have both generic and non-generic types with the same name, in the same namespace, you have to give them a slightly different name.
Change the model definition to:
var ts = TypeScript.Definitions()
.WithReference("Enums.ts")
.ForLoadedAssemblies();
ts.WithTypeFormatter((t, f) => GenerateTypeName(t, f, ts.ScriptGenerator));
Add this at the end:
<#+
private string GenerateTypeName(TsType tsType, ITsTypeFormatter f, TsGenerator gen)
{
TsClass tsClass = (TsClass) tsType;
string name = tsClass.Name;
if (!tsClass.GenericArguments.Any())
return name;
name += "_" + tsClass.GenericArguments.Count;
name += "<" + string.Join(",", tsClass.GenericArguments.Select(arg =>
{
string suffix = "";
var argCol = arg as TsCollection;
if (argCol != null)
{
suffix = string.Join("", Enumerable.Repeat("[]", argCol.Dimension));
}
return gen.GetFullyQualifiedTypeName(arg) + suffix;
})) + ">";
return name;
}
#>
It will generate:
declare module DTO {
interface ITest extends DTO.ITest_1<string> {
}
interface ITest_1<TKey> {
Id: TKey;
Value: string;
}
}