Variant type and non, with same name - typelite

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;
}
}

Related

Migration not starting with mongock 4.3.8

I am trying to create indexes on a mongodb collection using mongock version 4.3.8. The mongockLock and mongockChangeLog collections are eventually created, but the migration itself does not run. mongockChangeLog table is empty. Query db.BOOKS.getIndexes(); returns a single string: with an index for id (the value of name in it is equal to _id_), which is most likely generated automatically. There are no obvious errors in the logs either.
Everything else seems to be set up correctly, I don't understand why my migration is not running.
build.gradle.kts:
implementation("com.github.cloudyrock.mongock:mongodb-springdata-v3-driver:4.3.8")
implementation("com.github.cloudyrock.mongock:mongock-spring-v5:4.3.8")
implementation(platform("com.github.cloudyrock.mongock:mongock-bom:4.3.8"))
application.yml:
mongock:
change-logs-scan-package:
- com.dreamland.configuration.migration
config file:
#EnableMongock
#Configuration
#EnableMongoAuditing
class MongoConfig
Migration file:
#ChangeLog(order = "1668471203")
class IndexChangeLog {
companion object : KLogging()
#ChangeSet(order = "1668471204", id = "1668471204_create_indexes", author = "Irish")
fun createIndexes(mongockTemplate: MongockTemplate) {
val indexOps = mongockTemplate.indexOps(BookObjectDocument::class.java)
Index().on("createdAt", Sort.Direction.DESC).let { indexOps.ensureIndex(it) }.also { log(it) }
Index().on("createdBy", Sort.Direction.ASC).let { indexOps.ensureIndex(it) }.also { log(it) }
Index().on("type", Sort.Direction.DESC).let { indexOps.ensureIndex(it) }.also { log(it) }
}
private fun log(indexName: String) {
logger.info { "Index '$indexName' was created successfully" }
}
}
Document class:
#Document(collection = "BOOKS_V2")
data class BookObjectDocument(
val type: BookObjectType?,
val description: String?
) {
#Id
lateinit var id: String
#CreatedDate
lateinit var createdAt: Instant
#CreatedBy
var createdBy: String? = null
}

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

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,

Interested Challenge: Is it possible to make a class according to a given prototype?

Say: there is already a given schema definition object:
const schema = { prop1: { type: String, maxLength: 8 }, prop2... };
Is it possible that: without declare the interface for each schema object, make a respective class which can produce documents with prop1:string, prop2... extracted from the schema.
I expect something like this in my app:
// schema definition:
const PersonSchema = { name: { type: String, maxLength: 8 } };
// class factory
const PersonClass = SchemaClassFactory(PersonSchema);
// instance with props defined in schema.
let person1 = new PersonClass();
person1.name = 'Jack';
let person2 = new PersonClass();
person2.name = 3; // should be wrong hinted by tslint.
How can I achieve that?
You can create a class for the schema object using a mapped type and conditional types to extract the shape of the object from the schema.
A possible solution is below, I am not sure I covered all the ways you can defined the schema in mongoose, but this should get you stared:
const PersonSchema = {
name: { type: String, maxLength: 8 },
age: { type: Number },
title: String,
id: ObjectID
};
type PrimitiveConstructor<T> = {
new (...a: any[]): any;
(...a: any[]): T
}
type Constructor<T> = {
new (...a: any[]): T;
}
type ExtractType<T> = {
[P in keyof T] :
T[P] extends PrimitiveConstructor<infer U>? U :
T[P] extends { type: PrimitiveConstructor<infer U> } ? U:
T[P] extends Constructor<infer U> ? U :
T[P] extends { type: Constructor<infer U> } ? U:
never
}
function createClass<T>(schema: T): new (data?: Partial<ExtractType<T>>) => ExtractType<T> {
// The class will not have the fields explicitly defined since we don't know them but that is fine
return new class {
// Optional constructor for assigning data to the fields, you can remove this if not needed
constructor(data?: any){
if(data) {
Object.assign(this, data);
}
}
} as any;
}
var PersonClass = createClass(PersonSchema);
type PersonClass = InstanceType<typeof PersonClass>
let p = new PersonClass();
p.name ="";
p.name = 2; // error
p.id = new ObjectID(10);
let p2 = new PersonClass({
name: "",
});

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));
}

Bookshelf.js join multiple tables

I have multple models in bookself:
User (id, username)
Team (id, name)
Skill (id, name)
TeamSkill (id, skill_id, team_id)
TeamSkillUpvote (id, team_skill_id, user_id)
I would like to make this kind of query:
select team.id, team.name, team_skill_upvotes.user_id
from skill, team_skills, team
where skill.id = <THE_ID_OF_MY_MODEL>
and skill.id = team_skills.skill_id
and team_skills.team_id = team.id
and team_skills.id = team_skill_upvotes.team_skill_id
Anyone can help?
Thanks!
After searching I found the answer...It is built in a few steps:
1: Define relations for the TeamSkill:
export interface ITeamSkillRelations {
team: Team;
upvotes: Collection<TeamSkillUpvote>;
}
export class TeamSkill extends bookshelf.Model<TeamSkill>{
...
public relations: ITeamSkillRelations;
public upvotes(): Collection<TeamSkillUpvote> {
return this.hasMany(TeamSkillUpvote, TeamSkillUpvote.teamSkillIdAttribute/*team_skill_id*/);
}
public team(): Team {
return this.belongsTo(Team, TeamSkill.teamIdAttribute/*team_id*/);
}
...
}
2: The magic:
export class Skill extends bookshelf.Model<Skill> {
public getTeams(): Promise<ITeamOfASkill[]> {
var fetchOptions: CollectionFetchOptions = {
withRelated: [
TeamSkill.relatedTeamSkillUpvotesAttribute/*upvotes*/,
TeamSkill.relatedTeamAttribute/*team*/
]
};
return this.teamSkills()
.fetch(fetchOptions)
.then((teamSkillsCollection: Collection<TeamSkill>) => {
var teamSkills: TeamSkill[] = teamSkillsCollection.toArray();
return _.map(teamSkills, _skill => this._convertTeamSkillToTeamOfASkill(_skill));
});
}
private _convertTeamSkillToTeamOfASkill(teamSkill: TeamSkill): ITeamOfASkill {
var team: Team = teamSkill.relations.team;
var upvotesCollection: Collection<TeamSkillUpvote> = teamSkill.relations.upvotes;
var upvotes: TeamSkillUpvote[] = upvotesCollection.toArray();
var upvotingIds =
_.map(upvotes, _ => _.attributes.user_id);
return {
team: team,
upvotingUserIds: upvotingIds
};
}
}