amqplib sendToQueue() is not sending messages to RabbitMQ - node.js

I am building a Typescript package around amqplib's Promise API to make it simpler for me to send messages between two RabbitMQ queues.
This is a method inside a class of said package that is responsible for sending a message to RabbitMQ. It's not actually sending messages, because I can't see them from CloudAMQP.
class PostOffice {
connection: any;
constructor(connection: any) {
this.connection = connection;
}
//...
// TMQMessage is an object that can be trivially serialized to JSON
// The Writer class holds the channel because I'm having trouble
// making Typescript class member updates stick after a method returns.
async send(writer: Writer, message: TMQMessage) {
//...RabbitMQ code begins here
let channel = writer.stageConsume[0]; // This channel is created elsewhere
// Queue is created here and I can see it in CloudAMQP.
await channel.assertQueue(message.to + "/stage/1", {
durable: true,
}); // `message.to` is a string indicating destination, e.g. 'test/hello'
// According to docs, the content should be converted to Buffer
await channel.sendToQueue(message.to + "/stage/1", Buffer.from(JSON.stringify(message)));
channel.close()
}
}
This is a class I made that creates connections and channels, which is being used here:
import { Writer } from "./Writer";
export class ConnectionFactory {
url: string;
amqp: any;
constructor(url: string) {
this.url = url;
this.amqp = require('amqplib');
}
async connect(timeout: number = 2000) {
let connection = await this.amqp.connect(this.url, {
// timeout for a message acknowledge.
timeout, // Make the timeout 2s per now
})
return connection;
}
async createChannels(connection: any, writer: Writer) {
//... relevant code starts here
let stageChannels = [];
stageChannels.push(await connection.createChannel())
writer.stageConsume = stageChannels;
return writer;
}
}
}
My connection factory seems to be working properly, because I can see the connections from CloudAMQP's dashboard. Also I can see the Queues that have been created (asserted in my code) from the dashboard too. However, I am unable to get amqplib to send a message out to CloudAMQP.
Here's the (async) code I'm using to call my package:
let Cf = new ConnectionFactory(url)
let connection = await Cf.connect()
let writer = new Writer();
let message: TMQMessage = {
//... message content in JSON
}
writer = await Cf.createChannels(connection, writer)
let po = new PostOffice(connection)
await po.send(writer, message)
What seems to be wrong?

Probably want to await the close method too. But in general I would recommend amqp-client.js (that also have TypeScript definitions), https://github.com/cloudamqp/amqp-client.js/

Related

How to do chained node-redis transactions with singleton wrapper functions

I have a Typescript-backed Express.js project that uses a singleton Redis client.
The singleton includes wrapper functions to Redis commands needed for my application (e.g., SADD).
Singleton
Here is a snippet of my singleton Redis client service, which is relevant to my question:
/**
* Set up a singleton class instance to interface
* with the Redis database, along with helper async
* functions that provide functionality.
*/
var redis = require("redis");
// https://github.com/redis/node-redis/issues/1673
type RedisClientType = ReturnType<typeof redis.createClient>;
type RedisClientOptionsType = Parameters<typeof redis.createClient>[0];
export class Redis {
private static instance: Redis;
private static client: RedisClientType;
constructor() {
if (Redis.instance)
return Redis.instance;
Redis.instance = this;
Redis.client = null;
}
/* ... */
async initializeClient(options: RedisClientOptionsType) {
Redis.client = redis.createClient(options);
Redis.client.on('connect', function() {
Redis.instance.log('Client connected');
});
Redis.client.on('error', function(err: Error) {
Redis.instance.log(`Could not communicate with Redis client [${err}]`);
});
await Redis.client.connect();
}
async shutdownClient() {
await Redis.client.quit();
}
async multi() {
await Redis.client.multi();
}
async exec() {
await Redis.client.exec();
}
/* ... */
async sAdd(k: string, v: string) {
return await Redis.client.sAdd(k, v);
}
}
Individual calls to sAdd, sMembers, etc. work fine. So the client itself is initialized correctly, and it is able to process basic Redis calls.
What I would like to do is perform some chained transactions, e.g., from a rudimentary POST request using the singleton Redis client service, process some data from an uploaded file, and then add some key-value pairs (to start):
import { Redis } from '#/service/redis';
/* ... */
export const myPost = async (req: Request, res: Response) => {
const redis = new Redis(); // initialized client, as defined above
const k = 'my-key';
const v = 'my-value';
await redis
.multi()
.sAdd(k, v)
.exec();
}
Problem
The problem is that I get two errors with the chained await ... call.
First error
The first error is related to the await keyword, just before the multi/sAdd/exec call:
'await' expressions are only allowed within async functions and at the top levels of modules.ts(1308)
This await is within the async-ed post function. (I assume that I need to this handle the results of the underlying Promise chain.)
Second error
The second error is related to the sAdd wrapper:
Property 'sAdd' does not exist on type 'Promise<void>'.ts(2339)
I tried to add a return type to the call to multi:
async multi(): Promise<RedisClientType> {
await Redis.client.multi();
}
But this did not resolve the error with sAdd.
Question
What changes do I make to the service/singleton, which would allow calls to wrapper functions to be chained?

Suggestion to Build a multiplayer Texas Holdem Poker game using NodeJS, Flutter

I have been building basic and complex mobile apps in Android and Flutter also have knowledge of NodeJS. I have already built a basic NodeJs multiplayer server for Texas Holdem Poker. Mulitple rooms and table logic are still remaining.
I want to develop a client mobile app in Flutter as I have deep knowledge of flutter.
I have been exploring the packages and convenient tools that are going to be used in Flutter but am still clueless.
This game development in Flutter is a new challenge for me so I would like to ask whether the technology stack for such a game is good or not?
Shall I consider switching from Flutter to something else?
I'd say go for it. I created a card game myself while learning Flutter - just needed a fun project to see it end to end.
Similarly to you, I used node.js in the backend, enabling me to play the game with my friends.
I ended up going with GraphQL for my back end services (using AWS AppSync service). Since GraphQL will give you push notification over websockets, it was ideal to send data between different clients playing the game. Node.js deployed on AWS Lambda with Dynamo DB for persistence worked without a problem.
I also ended up doing an AI algorithm (using Monte Carlo Tree Search) that enabled me to play the game against the AI. And as the final bit - I ported my back end node.js interface to dart, so I could play the game against the AI fully off-line.
I think I spent the most time figuring out how to do the card animation, especially since I wanted for the card moves to happen sequentially. AI would sometimes play the moves too fast, so there were multiple cards flying around at the same time. Once this was done - the rest of it was easy.
If you need more details, let me know. I'm happy to share bits of the code with you.
Edit: here are some code details. I'll try to edit out parts of code that you won't need, so probably some of it will fail to compile at first... And keep in mind - some things may be overcomplicated in my code - as I got the things up and running I would move on, so there is a lot to improve here...
First the back-end. As I mentioned, I used AWS: Dynamo DB to store the game data; a single lambda function to do all the work; and AppSync API: simply because it offers message push to the clients - you don't need to keep polling the back-end to see if there are any changes. And finally - I used Cognito user pool to authenticate users. In my app you can create your account, validate it through email etc.
I used AWS Amplify to setup a back-end: this is AWS framework that enables you to easily deploy your back-end even if you don't know that much about AWS. It is actually meant for app developers that don't need or want to learn about AWS in detail. Even if you know your way around AWS - it is still very useful, since it will automate a lot of security wiring for you, and will help you automate the deployment.
Currently, there is official amplify-flutter package; at the time I did this I used 3rd party package https://pub.dev/packages/amazon_cognito_identity_dart_2.
Now I'm assuming that you got your back-end setup: you deployed your node.js code in Lambda, and now you need your AppSync schema. Here's mine:
type Match #aws_iam #aws_cognito_user_pools {
matchId: Int
matchData: AWSJSON!
}
type PlayEvent #aws_iam #aws_cognito_user_pools {
matchId: Int!
success: Boolean!
playerId: Int!
eventType: String!
inputData: AWSJSON
eventData: AWSJSON
matchData: AWSJSON
}
input PlayEventInput {
matchId: Int!
eventType: String!
eventData: AWSJSON
playerId: Int
}
type Query {
getMatch(matchId: Int): Match #function(name: "tresetaFv2-${env}") #aws_iam #aws_cognito_user_pools
}
type Mutation {
playEvent(input: PlayEventInput!): PlayEvent #function(name: "tresetaFv2-${env}") #aws_iam #aws_cognito_user_pools
}
type Subscription {
onPlayEvent(matchId: Int, success: Boolean): PlayEvent #aws_subscribe(mutations: ["playEvent"])
}
As you can see, I'm using AWSJSON data type a lot - instead of coding my entire game schema in GraphQL, I'm simply passing JSON back and forth.
Few things to understand:
MatchData type holds your game state: what card each player holds, what are the cards in the deck, on the table, who's turn is it to play etc.
Query getMatch will be fired by the client app after selecting the match from the active matches list. When you join the game, this is how you fetch the game state.
Mutation playEvent is how you play your move: you pass PlayEventInput type - specifying matchID and playerID, event Type (in your case you can play card, fold, call...). The playEvent Mutation will return PlayEvent - telling you if the move was successful (was it legal at the time? Was your turn to play?), and it will return MatchData - the new game state after the move was played.
And finally - Subscription onPlayEvent. After each client joins the match (by match ID), it will subscribe to this subscription. As you can see from the subscription definition - it will subscribe to playEvent mutation: each time a player plays a move, it will notify all the others about the move - and pass on the result of the Mutation, so everyone will get the full game state to refresh their UI. A nice trick here is - you subscribe to mutations that have success=true, so you don't push any message for failed moves.
You will also see some annotations - this is how you tell Amplify to wire things for you:
#function(name: "tresetaFv2-${env}"): you tell it to call Lambda funciton called "tresetaFv2-${env}" to actuall do the work
#aws_iam #aws_cognito_user_pools - this is how you tell it that this API is scured by Cognito user pools.
So now we have the AWS back-end setup. So how do you actually run the query and subscribe to the events coming from the back-end?
First, I use this UserService in my code: https://github.com/furaiev/amazon-cognito-identity-dart-2/blob/main/example/lib/user_service.dart
This is my AppSync Service - this is just a generic way of calling AppSync:
import 'dart:convert';
import 'package:amazon_cognito_identity_dart_2/cognito.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:treseta_app/auth/auth_services.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class AppSyncImpl {
final String _endPoint;
String get _wsEndPoint => _endPoint.replaceAll('https', 'wss').replaceAll('appsync-api', 'appsync-realtime-api');
String get _host => _endPoint.replaceAll('https://', '').replaceAll('/graphql', '');
UserService _userService;
set userService(UserService userService) => this._userService = userService;
AppSyncImpl(this._endPoint, this._userService);
Future<http.Response> getHttpResponse(Map<String, String> query) async {
CognitoUserSession session = await _userService.getSession();
return http.post(
_endPoint,
headers: {
'Authorization': session.getAccessToken().getJwtToken(),
'Content-Type': 'application/json',
},
body: json.encode(query),
);
}
Future<Map<String, dynamic>> glQueryMutation(Map<String, String> query) async {
http.Response response;
try {
response = await getHttpResponse(query);
} catch (e) {
print(e);
}
return json.decode(response.body);
}
int wsTimeoutInterval;
void disconnectWebSocketChannel(WebSocketChannel wsChannel, String uniqueKey) {
wsChannel.sink.add(json.encode({'type': 'stop', 'id': uniqueKey}));
}
Future<WebSocketChannel> wsSubscription(
{#required Map<String, String> query,
#required String uniqueKey,
Function(Map<String, dynamic>) listener}) async {
var jwtToken = (await _userService.getSession()).getIdToken().getJwtToken();
var header = {'host': _host, 'Authorization': jwtToken};
var encodedHeader = Base64Codec().encode(Utf8Codec().encode(json.encode(header)));
// Note 'e30=' is '{}' in base64
var wssUrl = '$_wsEndPoint?header=$encodedHeader&payload=e30=';
var channel = IOWebSocketChannel.connect(wssUrl, protocols: ['graphql-ws']);
channel.sink.add(json.encode({"type": "connection_init"}));
channel.stream.listen((event) {
var e = json.decode(event);
switch (e['type']) {
case 'connection_ack':
wsTimeoutInterval = e['payload']['connectionTimeoutMs'];
var register = {
'id': uniqueKey,
'payload': {
'data': json.encode(query),
'extensions': {'authorization': header}
},
'type': 'start'
};
var payload = json.encode(register);
channel.sink.add(payload);
break;
case 'data':
listener(e);
break;
case 'ka':
print('Reminder: keep alive is not yet implemented!!!');
break;
case 'start_ack':
print('Ws Channel: Subscription started');
break;
default:
print('Unknown event $event');
}
}, onError: (error, StackTrace stackTrace) {
print('Ws Channel: $error');
}, onDone: () {
channel.sink.close();
print('Ws Channel: Done!');
});
return channel;
}
}
And this is the actual back-end service; notice how there is a function that corresponds to each query, mutation and subscription in GraphQL Schema (getMatchData, playEvent, subscribeWS):
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:treseta_app/appsync/app_sync.dart';
import 'package:treseta_app/auth/auth_services.dart';
import 'package:treseta_app/models/api_models.dart' as api;
import 'package:meta/meta.dart';
import 'package:treseta_app/models/game_model.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class AwsBackendService extends ChangeNotifier {
final subscriptionStreamCtrl = StreamController<api.PlayEvent>.broadcast();
Stream get subscriptionStream => subscriptionStreamCtrl.stream;
UserService userService;
String endPoint;
AppSyncImpl _appSyncImpl;
BuildContext context;
AwsBackendService({#required this.context, this.userService, this.endPoint})
: _appSyncImpl = AppSyncImpl(endPoint, userService);
#override
Future<api.Match> getMatchData(int matchId) async {
final query = {
'operationName': 'GetMatch',
'query': '''query GetMatch { getMatch(matchId:$matchId){matchData, playerId}}'''
};
User user = await userService.getCurrentUser();
String username = user.name;
var matchData = await _appSyncImpl.glQueryMutation(query);
var match = api.Match.fromJson(matchData['data']['getMatch']);
match.matchData.username = username;
return match;
}
Future<api.PlayEvent> playEvent(int matchId, int playerId, String eventType, api.PlayEventInputData eventData) async {
var encoded = json.encode(eventData.toJson()).replaceAll("\"", "\\\"");
final query = {
'operationName': 'PlayEvent',
'query':
'''mutation PlayEvent { playEvent(input:{matchId:$matchId, eventType:"$eventType", eventData:"$encoded"}){matchId, eventData, success, playerId, eventType, inputData, matchData}}''',
};
api.PlayEvent result =
await _appSyncImpl.glQueryMutation(query).then((value) => api.PlayEvent.fromJson(value['data']['playEvent']));
User user = await userService.getCurrentUser();
String username = user.name;
result.matchData.username = username;
return result;
}
WebSocketChannel _wsClient;
String _wsUniqeKey;
Future<void> subscribeWs(int matchId, int playerId, MatchData model) async {
final query = {
'operationName': 'OnPlayEvent',
'query':
'subscription OnPlayEvent { onPlayEvent(matchId:$matchId, success:true) {matchId, success,playerId,eventType,inputData,eventData,matchData}}',
};
_wsUniqeKey = UniqueKey().toString();
_wsClient = await _appSyncImpl.wsSubscription(
query: query,
uniqueKey: _wsUniqeKey,
listener: (Map<String, dynamic> message) {
var playEvent = api.PlayEvent.fromJson(message['payload']['data']['onPlayEvent']);
subscriptionStreamCtrl.sink.add(playEvent);
});
return;
}
void disconnect() {
try {
if (_wsClient != null) {
_appSyncImpl.disconnectWebSocketChannel(_wsClient, _wsUniqeKey);
}
} catch (e) {
print(e);
}
}
#override
void dispose() {
super.dispose();
subscriptionStreamCtrl.close();
disconnect();
}
}
You will see that each event we receive from AppSync subscription is just pumped into a Stream object.
And finally, my main ChangeNotifier Provider that holds the match data and notifies the UI is something like this - you will see how the incoming subscription events are processed, and UI is notified that there's a new event to be animated - a card being thrown, or new hand dealt etc.
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:treseta_app/backend/backend_service.dart';
import 'package:treseta_app/models/api_models.dart';
import 'package:treseta_app/models/game_model.dart';
class Treseta extends ChangeNotifier {
MatchData model;
int playerId;
AwsBackendService _backendService;
StreamSubscription backendServiceSubscription;
AwsBackendService get backendService => _backendService;
set backendService(BackendService value) {
if (backendServiceSubscription != null) backendServiceSubscription.cancel();
_backendService = value;
// this is where we process the incoming subscription messages:
backendServiceSubscription = _backendService.subscriptionStream.listen((inEvent) {
PlayEvent playEvent = inEvent as PlayEvent;
playCard(
inData: playEvent,
playerId: playEvent.playerId,
card: playEvent.inputData.card);
});
}
int _matchId;
int get matchId => _matchId;
set matchId(int value) {
_matchId = value;
subscribeAndGetMatchData();
}
Future<void> disconnect() async => backendService.disconnect();
Future<MatchData> getMatchData() async {
var match = await backendService.getMatchData(matchId);
playerId = match.playerId;
model = match.matchData;
return model;
}
Future<void> subscribeAndGetMatchData() async {
if (matchId != null) {
disconnect();
await getMatchData();
await subscribe();
}
return;
}
Future<void> subscribe() async {
backendService.subscribeWs(matchId, playerId, this.model);
}
void playCard(
{GameCard card,
GlobalKey sourceKey,
GlobalKey targetKey,
int playerId = 0,
PlayEvent inData}) async {
// Animation notifier logic goes here...
}
#override
void dispose() {
disconnect();
backendServiceSubscription.cancel();
super.dispose();
}
}
There you go - I hope this helps.
There is another bit on how to animate the cards - the big challenge is to run your animation sequentially - even if the players are quick enough to play cards very quickly. If you want, I can post some details around it as well.
*** One more edit ****
I created a repo with a simple animation example. If I find time, I'll write some comments in Readme file...
https://github.com/andrija78/cards_demo

what is the best way or pattern to create a Wrapper class to abstract the use of RabbitMQ in node js

so I'm using RabbitMQ for some Projects and i noticed that i ll use some duplicate code all the Time that's why i decided to make a Wrapper Class or Interface that have some function to use RabbitMQ direct without repeating the code all the time. i began to do this yesterday and i already had some Problems since i wanted to use OOP and Javascript can be complicated when using OOP (at least i think so)
I began with creating a class IRAbbitMQ with function init to initialize a connection and create a channel, i knew that i cant use nested classes so instead i wanted to use Factory functions, i tried to make the connection and channel a part of the class IRabbitMQ properties but i dont know why that gave me undefined when i create an instance of it
class IRabbitMQ {
constructor() {
this.init(rabbitMQServer); // rabbitMQServer for example 'localhost//5672'
}
// establish a Connection to RAbbitMQ Server
async init(host) {
try {
let connection = await amqplib.connect(host);
let channel = await connection.createChannel();
channel.prefetch(1);
console.log(' [x] Awaiting RPC requests');
this.connection = connection;
this.channel = channel;
}
catch(err) {
console.error(err);
}
}
// Close the Connection with RabbitMQ
closeConnection() {
this.connection.close();
}
log() {
console.log(this.connection);
}
EventPublisher() {
function init(IRabbit, publisherName) {
if(!IRabbit.connection) {
throw new Error('Create an Instance of IRabbitMQ to establish a Connection');
}
let ch = IRabbit.channel;
console.log(ch);
}
return {
init : init
}
}
}
var r = new IRabbitMQ();
r.log();
when i run the code the output is undefined, i dont know why since i m initializing the connection and channel properties in the init function and then called that function in the constructor so that should be initialized when i create an object of the Wrapper class. i wanted also to take some advices from you wether it is good to use classes or is there any other better way to create a Wrapper class or Interface for RabbitMQ to make it easy to use it and not have to duplicate Code.
Not really an answer, but I was able to successfully log the connection with this example code. I trimmed out other code to just focus on the .log() part that was logging a undefined.
Code is far from perfect, but works at least
const amqplib = require('amqplib');
class IRabbitMQ {
constructor() { }
async init(host) {
try {
const connection = await amqplib.connect(host);
const channel = await connection.createChannel();
channel.prefetch(1);
console.log(' [x] Awaiting RPC requests');
this.connection = connection;
this.channel = channel;
}catch(err) {
console.error(err);
}
}
log() {
console.log(this.connection);
}
}
async function createInstance(){
const instance = new IRabbitMQ();
try {
await instance.init('amqp://localhost');
}catch (e) {
throw new Error('OOPS!');
}
return instance;
}
async function runLogic() {
const r = await createInstance();
r.log();
}
runLogic().catch(console.log);
Just comment if you'd want me to give additional advice/tips, but this seems to work for me.

How can we load all messages from a single discord channel?

I'm currently working on a self-bot that fetches all images from a channel and then downloads them: when I use my self-bot, the bot doesn't fetch messages that aren't loaded by the client and we can't load all of the messages simultaneously. Is there a way to do that? Something like a command to load all messages from a channel and then do multiple .fetchMessages() to get them all?
Self-Bots might be against the ToS, but iterating through messages in a channel is not, as far as I know. So...
Here's a snippet that will fetch all messages using the new js async generators functionality for efficiency
The snippet:
async function * messagesIterator (channel) {
let before = null
let done = false
while (!done) {
const messages = await channel.messages.fetch({ limit: 100, before })
if (messages.size > 0) {
before = messages.lastKey()
yield messages
} else done = true
}
}
async function * loadAllMessages (channel) {
for await (const messages of messagesIterator(channel)) {
for (const message of messages.values()) yield message
}
}
How it's used:
client.on('ready', async () => {
const targetChannel = client.guilds.cache.first().channels.cache.find(x => x.name === 'test')
// Iterate through all the messages as they're pulled
for await (const message of loadAllMessages(targetChannel)) {
console.log(message.content)
}
})
We can't since it's against ToS. :/ (even if it's a bot I think)

TypeORM create connection globally

I am using typeorm with typescript in my node Js application. I am trying to figure out the way of using the single DB connection for all functions in the class. For example, I have two functions my class and want to use the global/single connection for all functions instead of creating a connection in every function as shown below:
export class SQLDBService implements IDatabaseService{
private readonly logger = getLogger("SQLDBService");
private connection:Connection;
getConversation(conversationId: string): ConversationEntity {
let conversationEntity = new ConversationEntity();
createConnection(/*...*/).then(async connection => {
let dbObj = await connection.getRepository(ConversationEntity).findOne({
conversationId: Equal(conversationId)
});
if(dbObj)
conversationEntity = dbObj;
});
return conversationEntity;
}
pushWrapUp(conversationId: string, wrapUp: string): void {
createConnection().then(async connection => {
let conversationEntity = await connection.getRepository(ConversationEntity).findOne({
conversationId: Equal(conversationId)
});
if(conversationEntity){
conversationEntity.wrapUp = wrapUp;
conversationEntity.endTime = new Date();
await connection.manager.save(conversationEntity);
}
});
}}
Can someone point me in the right direction?
The code above doesn't efficiently use async..await because promises aren't chained, this results in poor error handling and improper control flow.
As the documentation explains,
TypeORM's Connection does not setup a database connection as it might seem, instead it setups a connection pool. <...> Connection pool setup is established once connect method of the Connection is called. connect method is called automatically if you setup your connection using createConnection function. Disconnection (closing all connections in the pool) is made when close is called. Generally, you must create connection only once in your application bootstrap, and close it after you completely finished working with the database.
createConnection is supposed to be called only once on application initialization. Since it's asynchronous, initialization routine should wait for it before using TypeORM models.
As the documentation suggests, getConnection() can be used instead of createConnection. Since the purpose is to get a repository for default connection, getRepository can be used instead:
It's:
import {getRepository} from "typeorm";
...
async getConversation(conversationId: string): ConversationEntity {
let conversationEntity = new ConversationEntity();
let dbObj = getRepository(ConversationEntity).findOne({
conversationId: Equal(conversationId)
});
if(dbObj) conversationEntity = dbObj;
return conversationEntity;
}
You should use a global connection pool which will create, hold, and take care the used connections for you. I am not familiar with node.js, so I cannot give out a name of this kind 3rd party library. But there must be some, since the connection pool is a widely accepted design pattern.
this quite minimal refactoring should do the trick
export class SQLDBService implements IDatabaseService {
private readonly logger = getLogger("SQLDBService");
private connection:Connection;
init() {
this.connection = await createConnection(/*...*/)
}
getConversation(conversationId: string): ConversationEntity {
let conversationEntity = new ConversationEntity();
let dbObj = await this.connection.getRepository(ConversationEntity).findOne({
conversationId: Equal(conversationId)
});
if(dbObj)
conversationEntity = dbObj;
return conversationEntity;
}
pushWrapUp(conversationId: string, wrapUp: string): void {
let conversationEntity = await this.connection.getRepository(ConversationEntity).findOne({
conversationId: Equal(conversationId)
});
if(conversationEntity){
conversationEntity.wrapUp = wrapUp;
conversationEntity.endTime = new Date();
await this.connection.manager.save(conversationEntity);
}
}
}
const db = new SQLDBService()
try {
await db.init()
}
catch (error) {
console.error("db connection error")
console.error(error)
console.error("db connection error")
}

Resources