typeorm convert EntitySchema to Entity - node.js

I'm using typescript and typeorm. I have this Entity:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
#Entity()
export class Sample {
#PrimaryGeneratedColumn()
id: number;
#Column({ length: 50 })
name: string;
#Column('text', { nullable: true })
description: string;
}
I query a single result like this:
const connection = await this.getConnection();
const sampleRepo = await connection.getRepository(Sample);
const sample = await sampleRepo.createQueryBuilder('sample')
.where('sample.id = :id', { id: id })
.getOne();
Now, I need to do some stuff with the result columns, but the sample object is of type EntitySchema. So, in typescript, I can't do sample.id because the error:
Property 'id' does not exist on type 'EntitySchema<any>'
Is there anyway to convert the EntitySchema into an actual Sample object?

As it turns out, this is due to a bad implementation. I moved the creation of the repository to a separate class:
export default class Database {
private connectionManager: ConnectionManager
constructor() {
this.connectionManager = getConnectionManager();
}
public getRepository<T extends EntitySchema>(type: ObjectType<T> | EntitySchema<T> | string): Promise<Repository<T>> {
const connection = await this.getConnection();
return connection.getRepository(type);
}
public async getConnection(connectionName = 'default'): Promise<Connection> {
let connection: Connection;
if (this.connectionManager.has(connectionName)) {
connection = this.connectionManager.get(connectionName);
if (!connection.isConnected) {
connection = await connection.connect();
}
}
else {
const connectionOptions: ConnectionOptions = Object
.assign({ name: connection }, connectionProperties);
connection = await createConnection(connectionOptions);
}
return connection;
}
}
It looks like connection.getRepository doesn't return a promise. As well, the T generic shouldn't be extending EntitySchema. To make the function work as intended, I had to write it like this:
public getRepository<T>(type: ObjectType<T> | EntitySchema<T> | string): Promise<Repository<T>> {
return new Promise((resolve, reject) => {
this.getConnection().then(conn => {
resolve(conn.getRepository(type));
}).catch(reject);
});
}

Related

How should I create a PrismaAdapter

I would like to create a PrismaAdapter,the model should be a string or the PrismaModel (i dont know how to type it) but I am struggling and I don't know if it is even possible. Here is my failed attempt.
thx for your help
import { PrismaClient } from '#prisma/client'
//type PrismaModel = keyof PrismaClient<PrismaClientOptions>
//type PrismaModel = keyof Prisma.userDelegate<GlobalReject>
//type PrismaUserModel = Prisma.userDelegate<GlobalReject>;
class PrismaAdapter {
private prisma: PrismaClient
private user: PrismaUserModel
constructor() {
this.prisma = new PrismaClient()
this.user = this.prisma.user
}
async findOne(model: PrismaModel, where: object): Promise<any> {
return await this.prisma[model].findOne({ where })
}
async findMany(model: string): Promise<any[]> {
return await this.prisma[model].findMany()
}
async create(model: PrismaModel, data: object): Promise<any> {
return await this.prisma[model].create({ data })
}
async update(model: string, where: object, data: object): Promise<any> {
return await this.prisma[model].update({ where, data })
}
async delete(model: string, where: object): Promise<any> {
return await this.prisma[model].delete({ where })
}
}
export default PrismaAdapter
I am expecting to use it in a Database service class.
import PrismaAdapter from "./PrismaAdapter";
class DatabaseAdapter {
private database: PrismaAdapter;
private model: PrismaModel;
constructor({database, model}: {database: PrismaAdapter, model: PrismaModel}) {
this.database = database;
}
async findOne(model :PrismaModel,id: number): Promise<any> {
return this.database.findOne(model,where: {id})
}
async findMany(model: string): Promise<any[]> {
return await this.database.findMany(model)
}
}
export default DatabaseAdapter
And use this database Adapter in for exemple a UserRepository.

Is there a better way to define these funtions?

There has to be a better way of doing this.
I feel like I'm repeating myself.
Can anyone help? I'm a bit new to this. Is there some way that these different data functions and variables into a class that I can inherit from?
Thanks in advance!
import { Guild, GuildMember, TextChannel } from "discord.js"
import mafiaRoleSchema from "./models/mafiaRole-schema"
import willSchema from "./models/will-schema"
interface data<type>{
// MemberID: message
[key: string]: type
}
let willData = {} as data<string>
export async function setWillData(key : GuildMember, value: string) {
willData[key.id] = value
await willSchema.findOneAndUpdate({
_id: key.id
}, {
_id: key.id,
value
}, {
upsert: true
})
}
export async function getWillData(key : GuildMember): Promise<string | null>{
let data = willData[key.id]
if(!data){
const results = await willSchema.findById(key.id)
if (!results){
return null
}
const {text} = results
data = willData[key.id] = text
}
return data
}
let mafiaRoleData = {} as data<string>
export async function setmafiaRoleData(key : GuildMember, value: string) {
mafiaRoleData[key.id] = value
await mafiaRoleSchema.findOneAndUpdate({
_id: key.id
}, {
_id: key.id,
value
}, {
upsert: true
})
}
export async function getmafiaRoleData(key : GuildMember): Promise<string | null>{
let data = mafiaRoleData[key.id]
if(!data){
const results = await mafiaRoleSchema.findById(key.id)
if (!results){
return null
}
const {text} = results
data = mafiaRoleData[key.id] = text
}
return data
}
let welcomeData = {} as data<[TextChannel, string]>
export async function setwelcomeData(key : Guild, value: [TextChannel, string]) {
welcomeData[key.id] = value
const [target, text] = value
await mafiaRoleSchema.findOneAndUpdate({
_id: key.id
}, {
_id: key.id,
text,
channelId: target.id
}, {
upsert: true
})
}
export async function getwelcomeData(key : Guild): Promise<[TextChannel, string] | null>{
let data = welcomeData[key.id]
if(!data){
const results = await mafiaRoleSchema.findById(key.id)
if (!results){
return null
}
const {channelId, text} = results
const channel = key.channels.cache.get(channelId) as TextChannel
data = [channel, text]
}
return data
}
I've tried putting the functions into the interface. But that didn't work. I honestly don't know where to go.
I know it seems like a lot, but this isn't as bad as you think. Your welcome message code is sufficiently different than your other code, which merits it having its own functions.
As for the mafia and will data, you could do an abstraction kind of like this:
import mafiaRoleSchema from "./models/mafiaRole-schema"
import willSchema from "./models/will-schema"
interface Data<type>{
// MemberID: message
[key: string]: type
}
type Schema = typeof mafiaRoleSchema | typeof willSchema // this could be more generic if you have access to those types
class SchemaController {
private schema: Schema
private data: Data<string>
constructor(schema: Schema) {
this.schema = schema
this.data = {}
}
async setData(key: GuildMember, value: string) {
this.data[key.id] = value
await this.schema.findOneAndUpdate({
_id: key.id
}, {
_id: key.id,
value
}, {
upsert: true
})
}
async getData(key : GuildMember): Promise<string | null> {
let data = this.data[key.id]
if(!data){
const results = await this.schema.findById(key.id)
if (!results){
return null
}
const {text} = results
data = this.data[key.id] = text
}
return data
}
const mafiaController = new SchemaController(mafiaRoleSchema)
const willController = new SchemaController(willSchema)

Trying to call by Enum (NestJS)

I'm coding on NestJS and I'm trying to call my database info by enumeration.
Example: I want all of motherboard (motherboard is an enum). I'm having many options for this.
I would like your opinion and for you, what is the best option?
This is my Entity :
#Entity('component')
export class Component {
static idComponent(idComponent: any): Component[] | PromiseLike<Component[]> {
throw new Error('Method not implemented.');
}
#PrimaryGeneratedColumn()
idComponent: number;
#Column()
name: string;
#Column()
brand: string;
#Column()
availability: string;
#Column()
price: string;
#Column('enum', { enum: ComponentType })
type: ComponentType;
service_type: ComponentType;
#Column('datetime', { default: () => 'CURRENT_TIMESTAMP' })
date: string;
#ApiProperty({ enum: () => Configurateur })
#OneToMany(() => Configurateur, configurateur => configurateur.component)
ComponentType: Configurateur[];
}
My Controller :
#ApiTags('component')
#Controller('component')
export class ComponentController {
constructor(private componentService: ComponentService) { }
#Get('all')
async findAll(#Res() res) {
const lists = await this.componentService.findAll();
return res.status(HttpStatus.OK).json(lists);
}
#Get('id')
async findById(#Res() res, #Query('id') id: string) {
const lists = await this.componentService.findById(id);
if (!lists) throw new NotFoundException('Id does not exist!');
return res.status(HttpStatus.OK).json(lists);
}
#Get('type')
async findByType(#Res() res, #Query('type') ComponentType: string) {
const listsType = await this.componentService.findByType(ComponentType);
if (!listsType) throw new NotFoundException('Id does not exist!');
return res.status(HttpStatus.OK).json(listsType);
}
}
And my Service :
#Injectable()
export class ComponentService {
constructor(
#InjectRepository(Component)
private readonly componentRepository: Repository<Component>,
) { }
async findAll(): Promise<Component[]> {
return await this.componentRepository.find();
}
async findById(id): Promise<Component[]> {
const customer = await this.componentRepository.findByIds(id);
return customer;
}
async findByType(ComponentType): Promise<any> {
const customerType = await this.componentRepository.find(ComponentType);
return customerType;
}
// Have Error and is a test
getType(ByType): Promise<any> {
let Type = String(ByType);
return new Promise(resolve => {
const type = this.componentRepository.find(type => type.Type === Type);
if (!type) {
throw new HttpException('Course does not exist', 404)
}
resolve(Type)
});
}
}
Edit : CompentType is my Enums file.
Thank you for your time.

How to extend mongoose Query class with Typescript?

I'm trying to implement caching with Mongoose, Redis, and Typescript. My cache.ts file :
import mongoose, { model, Query } from "mongoose";
import redis from "redis";
//import { CacheOptions } from "../../types/mongoose";
type CacheOptions = { key?: string };
const client = redis.createClient();
const getCache = function (
hashKey: string,
key: string
): Promise<string | null> {
return new Promise((res, rej) => {
client.hget(hashKey, key, (err, val) => {
if (err) rej(err);
else res(val);
});
});
};
const exec = mongoose.Query.prototype.exec;
mongoose.Query.prototype.cache = function (options: CacheOptions = {}) {
this.useCache = true;
this.hashKey = JSON.stringify(options.key || "");
return this; //make cache() chainable
};
mongoose.Query.prototype.exec = async function () {
if (!this.useCache) {
//NO CACHE
return exec.apply(this);
}
const key = JSON.stringify({
...this.getQuery(),
collection: this.model.collection.name,
});
const cacheValue = await getCache(this.hashKey, key);
if (cacheValue) {
console.log("DATA FROM CACHE");
const doc = JSON.parse(cacheValue);
//convert plain object to mongoose object
return Array.isArray(doc)
? doc.map((d) => new this.model(d))
: new this.model(doc);
}
const result = await exec.apply(this);
client.hset(this.hashKey, key, JSON.stringify(result));
return result;
};
/**
*
* #param hashKey hashkey to remove
*/
const clearHash = (hashKey: string) => {
client.del(JSON.stringify(hashKey));
};
export { clearHash };
And this is my type declaration file under types folder: mongoose.d.ts
declare module "mongoose" {
export interface Query<
ResultType,
DocType extends Document,
THelpers = {}
> {
cache(): Query<T>;
useCache: boolean;
hashKey: string;
model: Model<T>;
}
}
VsCode IntelliSense doesn't give any warning or error. When I run the code I get following error:
TSError: тип Unable to compile TypeScript:
src/services/product/product.controller.ts:92:67 - error TS2551: Property 'cache' does not exist on type 'Query<IProduct | null, IProduct, {}>'. Did you mean 'catch'?
92 const foundProduct = await Product.findOne({ slug }, { __v: 0 }).cache();
I'm not sure if I correctly defined the types but it seems like TypeScript doesn't see my declaration or something else. If you have any suggestion I'll be appreciate.
one alternative option is that you can do is override that Query class in index.d.ts by adding cache: any
Here's how I solved this issue. In the same cache.ts file do this,
declare module 'mongoose' {
interface DocumentQuery<
T,
DocType extends import('mongoose').Document,
QueryHelpers = {}
> {
mongooseCollection: {
name: any;
};
cache(): DocumentQuery<T[], Document> & QueryHelpers;
useCache: boolean;
hashKey: string;
}
interface Query<ResultType, DocType, THelpers = {}, RawDocType = DocType>
extends DocumentQuery<any, any> {}
}
then,
mongoose.Query.prototype.cache = function (options: Options = {}) {
this.__cache = true;
this.__hashKey = JSON.stringify(options.key || '');
// To make it chain-able with the queries
// Ex: Blog
// .find()
// .cache()
// .limit(10)
return this;
};
Because I was not fully confident if the DocumentQuery<any, any> {} part is exactly correct so I didn't create it on a separate mongoose.d.ts file.
But this code will definitely work.

Why Typeorm does always fail when trying to insert to 2 tables and Math.random() returns always same number?

So i have following code:
import { getRepository } from "typeorm";
import { NextFunction, Request, Response } from "express";
import { Users } from "../entity/Users";
import { Verify } from "../entity/Verify";
import { VerifyController } from "./VerifyController";
export class UserController {
private userRepository = getRepository(Users);
private verifyRepository = getRepository(Verify);
// async all(request: Request, response: Response, next: NextFunction) {
// return this.userRepository.find();
// }
// async one(request: Request, response: Response, next: NextFunction) {
// var user = await this.userRepository.findOne(request.params.id);
// if(user) {
// return user;
// }
// return {Error: "Couldn't find user"};
// }
// async save(request: Request, response: Response, next: NextFunction) {
// return this.userRepository.save(request.body);
// }
// async remove(request: Request, response: Response, next: NextFunction) {
// let userToRemove = await this.userRepository.findOne(request.params.id);
// await this.userRepository.remove(userToRemove);
// }
async register(request: Request, response: Response, next: NextFunction) {
var body = request.body;
var isUsername = await this.userRepository.count({username: body.username});
if(isUsername > 0) {
return {Status: "ERROR", ERR: "Username already exists!"}
}
var isEmail = await this.userRepository.count({email: body.email});
if(isEmail > 0) {
return {Status: "ERROR", ERR: "Email already exists!"}
}
var loop = true;
var UserID = this.generate(10);
while(loop) {
var isUserID = await this.userRepository.count({userID: UserID});
if(isUserID > 0) {
UserID = this.generate(10);
} else {
loop = false;
}
}
body.userID = UserID;
this.userRepository.save(body);
var tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
var verifyID = this.generate(20);
loop = true;
while(loop) {
var verifyCount = await this.verifyRepository.count({verify_id: verifyID});
if(verifyCount > 0) {
loop = false;
} else {
verifyCount = this.generate(20);
}
}
this.verifyRepository.save({account_id: body.userID, verify_id: verifyID, validUntil: tomorrow });
return {Status: "OK", UserID: body.userID};
}
async nameCheck(request: Request, response: Response, next: NextFunction) {
var isUsername = await this.userRepository.count({username: request.body.name});
if(isUsername > 0) {
return {Status: "ERROR"};
}
return {Status: "OK"};
}
generate(n) {
var rN = "";
while(n > 0) {
rN += Math.floor(Math.random() * 10).toString(); // returns a random integer from 0 to 9
n--;
}
return parseInt(rN);
}
}
This is a function inside a TypeORM Controller. I've started with the default express template from TypeORM and added this function. I commented the other functions out because i dont use the default ones.
Anyway the function that doesn't seem to work as intended is register.
The problems i have with this function are the following:
I try to insert into two tables but it just inserts into the first one named "users" and the second one seems to be empty even after multiple atempts.
I use the function "generate" to genrate random user ids of length 10 and 20. But every time i restart my typeorm server and drop the database, the id is the same. Every time. The function always returns "2147483647" but when i try to debug it with console.log() i get a real random number. Also when trying to make a second number with length 20 it returns the same number 2147483647. And i dont know why
I realy hope someone could help me.
For better understanding i will also post the entity classes:
Users.ts:
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
#Entity()
export class Users {
#PrimaryGeneratedColumn()
id: number;
#Column()
username: string;
#Column()
userID: number
#Column()
email: string;
#Column()
firstName: string;
#Column()
lastName: string;
#Column()
birthday: Date;
#Column()
ipAdress: string;
#Column()
verified: boolean;
}
Verify.ts:
import { Col } from "react-bootstrap";
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
#Entity()
export class Verify {
#PrimaryGeneratedColumn()
id: number;
#Column()
verify_id: number;
#Column()
account_id: number;
#Column()
validUntil: Date;
}
Fixed it myself.
I did find the solution by myself and not on the internet.
The problem is that by default the int is set in mysql to int(10) which shorts my digit to the described one.
I tried to use bigint to solve this problem and it worked at least with the 10 digit number.
But it looks like you can't store 20 digit numbers in the database as actual int.
It is possible with Strings i think.

Resources