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

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: "",
});

Related

Error: Expected a function body or '=>'. Try adding {}

dart "c:\Users\yigit\OneDrive\Masaüstü\Tutorial Dart\13.02.2023\13.02.2023.dart"
../../Tutorial%20Dart/13.02.2023/13.02.2023.dart:8:7: Error: The non-abstract class 'Vehicle' is missing implementations for these members:
Vehicle.info
Try to either
provide an implementation,
inherit an implementation from a superclass or mixin,
mark the class as abstract, or
provide a 'noSuchMethod' implementation.
class Vehicle {
^^^^^^^
../../Tutorial%20Dart/13.02.2023/13.02.2023.dart:15:8: Context: 'Vehicle.info' is defined here.
void info();
^^^^
// Higher order and Lexure Closure
Function plusWith(num x) {
return (num y) => x + y;
}
class Vehicle {
String brand = "";
String model = "";
int horsePower = 0;
Vehicle(this.brand, this.model, this.horsePower);
}
class Car extends Vehicle {
Car(String brand, String model, int horsePower)
: super(brand, model, horsePower);
}
class Motorcycle extends Vehicle {
Motorcycle(String brand, String model, int horsePower)
: super(brand, model, horsePower);
}
main(List<String> args) {
//Lambda Functions
var func1 = (int v1) => v1 * 3;
print(func1(5));
// Lexure Closure Cont;
var addNum1 = plusWith(5);
var addNum2 = plusWith(7);
print(addNum1(2)); // Output 5 + 2 = 7
print(addNum2(3)); // Output 7 + 3 = 10
Vehicle car = Car(brand: "Golf", model: "VW", horsePower: 150);
Vehicle car2 = Car(model: "A4", brand: "Audi", horsePower: 150);
Vehicle moto = Motorcycle(brand: "MT-25", model: "Yamaha", horsePower: 37);
Vehicle moto2 =
Motorcycle(brand: "Super-Tenere", model: "Yamaha", horsePower: 127);
List<Vehicle> allVehicle = [car, car2, moto, moto2];
/* var list1 = List<Vehicle>.filled(5, Vehicle("", "", 0));
var listFrom = List<Vehicle>.from(allVehicle); */
var listOf = List<Vehicle>.of(allVehicle.whereType<Car>());
print(listOf);
/* print(listFrom); */
}
Nothing i've been tried
Just make Car and Motorcycle variables required as follows:
class Car extends Vehicle {
Car({required String brand,required String model,required int horsePower})
: super(brand, model, horsePower);
}
class Motorcycle extends Vehicle {
Motorcycle({required String brand, required String model, required int horsePower})
: super(brand, model, horsePower);
}

Loopback-next ReferenceMany Unable to find _id

I have user Model, which reference to ListedStocks,
#referencesMany(
() => ListedStocks,
{},
{
mongodb: {dataType: 'ObjectId'},
},
)
stockIds?: string[];
But when i try to Create a user and pass an Array of Stocks _id, it gives me an error.
Error: Entity not found: ListedStocks with id "constraint {\"_id\":[\"62eeb4b42b59f883f02f381b\"]}"
When i tried To Debug this, I saw this Query.
loopback:connector:mongodb all ListedStocks { where: { _id: [ '62eeb4b42b59f883f02f381b' ] } } null []
shouldn't where : {_id: []} be where: {_id: {$in: []}}
Or am i missing something?
The Issue was in the constraint created by the createReferencesManyAccessor
The constraint const constraint: any = {[primaryKey]: foreignKeyValue};
should have been constraint = {[primaryKey]: {inq:foreignKeyValue}};
Complete Fix is Below.
NOTE: This fix is specifically for the MongoDB, not sure if this breaks SQL Based Repos
export class TempReferencesManyRepository<
TargetEntity extends Entity,
TargetIds,
TargetRepository extends EntityCrudRepository<TargetEntity, TargetIds>,
> implements ReferencesManyRepository<TargetEntity>
{
/**
* Constructor of DefaultReferencesManyEntityCrudRepository
* #param getTargetRepository - the getter of the related target model repository instance
* #param constraint - the key value pair representing foreign key name to constrain
* #param foreignKeyValue - PrimaryKey for Constraint
* the target repository instance
*/
constructor(
public getTargetRepository: Getter<TargetRepository>,
public constraint: DataObject<TargetEntity>,
protected foreignKeyValue: any,
) {}
async get(options?: Options): Promise<TargetEntity> {
const targetRepo = await this.getTargetRepository();
const result = await targetRepo.find(
constrainFilter(undefined, this.constraint),
options,
);
let expectedLength = 1;
if(Array.isArray(this.foreignKeyValue)){
expectedLength = this.foreignKeyValue.length;
}
if (result.length !== expectedLength) {
// We don't have a direct access to the foreign key value here :(
const id = 'constraint ' + JSON.stringify(this.constraint);
throw new EntityNotFoundError(targetRepo.entityClass, id);
}
console.log("RESULT: ",result);
return result[0];
}
}
export function createReferencesManyAccessor<
Target extends Entity,
TargetIds,
Source extends Entity,
SourceId,
>(
referencesManyMetadata: ReferencesManyDefinition,
targetRepoGetter: Getter<EntityCrudRepository<Target, TargetIds>>,
sourceRepository: EntityCrudRepository<Source, SourceId>,
): ReferencesManyAccessor<Target, SourceId> {
const meta = resolveReferencesManyMetadata(referencesManyMetadata);
debug('Resolved ReferencesMany relation metadata: %o', meta);
const result: ReferencesManyAccessor<Target, SourceId> =
async function getTargetInstancesOfReferencesMany(sourceId: SourceId) {
const foreignKey = meta.keyFrom;
const primaryKey = meta.keyTo;
const sourceModel = await sourceRepository.findById(sourceId);
const foreignKeyValue = sourceModel[foreignKey as keyof Source];
// workaround to check referential integrity.
// should be removed once the memory connector ref integrity is done
// GH issue: https://github.com/loopbackio/loopback-next/issues/2333
if (!foreignKeyValue) {
return undefined as unknown as Target;
}
//FIXME: This is MONGODB Specific Fix, MIGHT BROKE in SQL.
// eslint-disable-next-line #typescript-eslint/no-explicit-any
let constraint: any = {[primaryKey]: foreignKeyValue};
if(Array.isArray(foreignKeyValue)){
constraint = {[primaryKey]: {inq: foreignKeyValue}};
}
const constrainedRepo = new TempReferencesManyRepository(
targetRepoGetter,
constraint as DataObject<Target>,
foreignKeyValue
);
return constrainedRepo.get();
};
result.inclusionResolver = createReferencesManyInclusionResolver(
meta,
targetRepoGetter,
);
return result;
}
export default class ReferenceManyCurdRepository<
T extends Entity,
ID,
Relations extends object = {}
> extends DefaultCrudRepository<T, ID, Relations>{
protected createReferencesManyAccessorFor<Target extends Entity, TargetId>(
relationName: string,
targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
): ReferencesManyAccessor<Target, ID> {
const meta = this.entityClass.definition.relations[relationName];
return createReferencesManyAccessor<Target, TargetId, T, ID>(
meta as ReferencesManyDefinition,
targetRepoGetter,
this,
);
}
}

How model a updatable view for a dynamic form builder and listen to their changes

I'm building a server-driven UI (aka: a form builder) that get the form definition dynamically in JSON, then renders that form.
I need to update the values and send back the results. Some fields also need to trigger validations, so I need to listen to them.
Doing this with JetPack is confusing (to me) because it is not clear how I architect the form builder.
The data for the form builder is:
#Serializable
enum class Action {
Create,
Update,
}
#Serializable
data class ErrorMsg(
val message: String,
val kind: ErrorKind
)
#Serializable
data class Form(
var main_action: Action?,
var title: String?,
var errors: ArrayList<ErrorMsg>
var sections: ArrayList<FormSection>,
)
// A form section is a group of fields
#Serializable
data class FormSection(
var id: UInt,
var fields: ArrayList<FormField>,
var title: String?
)
#Serializable
data class FormField(
val field: String,
val obligatory: Boolean,
val error: ErrorMsg?,
var value: String, //<-- This is what needs to trigger changes!
)
So, if I try to pass the form to a view:
#Composable
fun ComposablePreview() {
val f1 = UIData.FormField.txt("1", "Code", "001")
val f2 = UIData.FormField.pwd("2", "Pwd", "123")
val fields = arrayListOf(f1, f2)
val sections =
UIData.FormSection(
id = 0u, fields = fields, "Sample")
val form_base = UIData.Form(
main_action = null, sections= arrayListOf(sections))
val form by remember { mutableStateOf(form_base) }
FormView(form = form)
}
How get the changes propagated?
I build each form control alike
#Composable
fun TextCtrlView(
field: UIData.FormField,
) {
var value by remember { mutableStateOf(field.value) }
TextField(
label = { Text(field.label) },
value = value,
isError = field.error != null,
onValueChange = { value = it },
)
}

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

Class is undefined on typeScript

i need to create a object. class & their properties and object creation happen on different files.
this is the file that include class
module Model {
export class DonationServiceResponse {
ErrorMessage: string;
ErrorCode: number;
Comapny: string;
StreateName: string;
IsAnonymous: boolean;
IsOneOffDonation: boolean;
DonationIntentType: number;
EmployeeId: number;
Amount: number;
LogoImage: string;
Id: number;
}}
in below file object creation is happened
/// <reference path="DonationServiceResponse.ts" />
/// <reference path="../../typings/angularjs/angular.d.ts" />
angular.module('myApp', []);
interface IDonationServiceController {
Tiles: Array;
TileContainerEntities: Array;
TotalAmount: number;
}
class TileContainerEntity {
DonationContainer: test.DonationServiceResponse;
httpService: any;
scope: any;
res: boolean;
AnonymousText: string;
OneTimeText: string;
constructor(donationAmount: number, charityLogo: string, anonymous: bool, oneTime: bool, id: number, employeeId: number, http: ng.IHttpService, scope: ng.IHttpService) {
this.DonationContainer = new test.DonationServiceResponse();
this.DonationContainer.Amount = donationAmount;
this.DonationContainer.LogoImage = charityLogo;
this.DonationContainer.Id = id;
this.DonationContainer.EmployeeId = employeeId;
this.httpService = http;
this.scope = scope;
}
}
class DonationServiceController implements IDonationServiceController {
private httpService: any;
TotalAmount: number = 0;
Tiles: TileContainerEntity[];
TileContainerEntities: TileContainerEntity[];
constructor($scope, $http: ng.IHttpService) {
this.httpService = $http;
$scope.VM = this;
this.getAllTiles($scope.VM, $http);
}
getAllTiles(scope, http): void {
this.httpService.get("/API/PrivateAPI/test").success(function (data, status) {
this.TileContainerEntities = [];
var num = 0;
for (var index = 0; index < data.length; index++) {
var amount = data[index].Amount;
var chairtyLogo = data[index].LogoImage;
var isAnonymousOff = data[index].IsAnonymous;
var isOneOff = data[index].IsOneOffDonation;
var id = data[index].Id;
var empId = data[index].EmployeeId;
var tileEntity = new TileContainerEntity(amount, chairtyLogo, isAnonymousOff, isOneOff, id, empId, http, scope);
this.TileContainerEntities.push(tileEntity);
num = num + data[index].Amount;
}
scope.Tiles = this.TileContainerEntities;
scope.TotalAmount = num;
});
}
}
my problem is in side the constructor DonationContainer shows as undefined object. it is not initialize. if i use one file for this thing(there are not two files) its works well.Does anyone know how to do so and reason for this ?
When you want the two typescript files to be merged into a single javascript file you need to specify the --out compiler flag.
Otherwise each typescript file is compiled into a separate javascript file and you are responsible for loading them in the right order.