Is there a better way to define these funtions? - node.js

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)

Related

TypeScript: API alway return empty with object { [key: string]: any }

I have issue when response data from API alway empty.
Sample code below:
interface DataStore {
[key: string]: any,
}
static GetData = async (req: Request, res: Response): Promise<Response> => {
let obj: DataStore = [];
obj['id'] = 'account';
obj['pwd'] = 'password';
console.log(obj); // can see data: [ id: 'account', pwd: 'password' ]
res.status(200).send(obj); // it return empty: []
}
I don't know why. Has anyone encountered this, and how to handle it? Please let me know. Thank you.
You should set your obj to an empty object {}:
const t1 = []
t1['foo'] = 'foo'
t1['bar'] = 'bar'
console.log(JSON.stringify(t1)) // []
const t2 = {}
t2['foo'] = 'foo'
t2['bar'] = 'bar'
console.log(JSON.stringify(t2)) // {foo: 'foo', bar: 'bar'}
So, in your code, do the following:
interface DataStore {
id?: string,
pwd?: string,
}
// No need for `async` here since there is no `async` code here
static GetData = (req: Request, res: Response): void => {
const obj: DataStore = {};
obj['id'] = 'account';
obj['pwd'] = 'password';
res.status(200).json(obj);
}

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.

Sequelize,findAll, attributes can not rename column

My Table data:
Mytable(id,col1,col2,time)
with data
Mytable("1","val1","val2","time")
I fetch data by this code:
async function getData() {
const mytables = await Mytable.findAll(
{ where: { id: othervalue }},
{ attributes: ['col1',['id', 'newnameid'],'newnamecol1']}
);
console.log(JSON.stringifty(mytables));
}
getData();
I want data only like this:
[ {'newnameid':'1','newnamecol1':'val1'} ]
But I got this:
[ {'id':'1','col1':'val1','col2':'val2,'time':'time''} ]
Uhm, what is possible wrong with this code ?
async function getData() {
const mytables = await Mytable.findAll(
{
where: { id: othervalue },
attributes: ['col1',['id', 'newnameid'],'newnamecol1']
},
);
console.log(JSON.stringifty(mytables));
}
getData();
Updated:
attributes: ['col1',['id', 'newnameid'],'newnamecol1'] -> inside first {}
Use findAll and
const convertedData = await rows.map(arrObj => {
return {
newname: reqbody.olddata
}})

How to pass Model sequelize as parameter?

i try to pass a model class as parameter, but typescript doesn't see methods, properties.
import type { Model } from 'sequelize';
const isExistsValidator = (model: typeof Model, field: string) => {
return async (value): boolean => {
const result = await model.findOne({ where: { [field]: value } }); // No overload matches this call.
// ...
};
};
I have tried some variants:
import type { Model, ModelStatic } from 'sequelize';
const isExistsValidator = <M extends Model>(model: ModelStatic<M>, field: string) {
// ...
const result = await model.findOne({ where: { [field]: value } });// Property 'findOne' does not exist on type
}
import type { Model } from 'sequelize';
const isExistsValidator = (model: Model, field: string) {
// ...
const result = await model.findOne({ where: { [field]: value } });// Property 'findOne' is a static member of type 'Model<any, any>'
How it will be use (something like this):
const isExistsName = isExistsValidator(UserModel, 'name');
// ...
if(await isExistsName('alex4answer')) {
throw new Error('Name already in use');
}
What am i doing wrong?
i'm new in typescript

typeorm convert EntitySchema to Entity

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

Resources