TimelordUK / jspurefix

native typescript FIX engine
MIT License
58 stars 27 forks source link

Help with using the send function from different class #43

Closed bekee closed 2 years ago

bekee commented 2 years ago

Please I need help with calling the send function of the session from a controller so I can send request via api to the server. A guide or an example would be grateful.

@TimelordUK

bekee commented 2 years ago

I've tried resolving the TradeClient class but still not able to send request to the server.

import {container} from "tsyringe"; import 'reflect-metadata' import { TradeCaptureClient } from "@fix/trade-capture-client"

export class NewsController  {

   testNews = async (req: Request, res: Response, next: NextFunction) => {

        const tradingSession = container.resolve(TradeCaptureClient)

        const headline = req.params.headline

        await tradingSession.sendNews(headline);

        return res.status(200).send("sent")
    };
}
}

@injectable()
// @singleton()
export class TradeCaptureClient extends AsciiSession {
  private readonly logger: IJsFixLogger
  private readonly fixLog: IJsFixLogger
  private reports: Dictionary<ITradeCaptureReport>

  constructor (@inject( DITokens.IJsFixConfig)  public readonly config: IJsFixConfig) {
    super(config)
    this.logReceivedMsgs = true
    this.reports = new Dictionary<ITradeCaptureReport>()
    // this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`)
    this.fixLog = config.logFactory.plain(`jsfix.client.${new Date().toDateString()}.txt`)
    this.logger = config.logFactory.logger(`${this.me}:TradeCaptureClient`)

  }

//.....

 public sendNews(headline: string){
    this.send(MsgType.MarketDefinitionRequest, NewsFactory.requestNews(headline))
  }
}

I keep getting the error


{
    "error": "Cannot inject the dependency \"config\" at position #0 of \"TradeCaptureClient\" constructor. Reason:\n    Attempted to resolve unregistered dependency token: \"IJsFixConfig\""
}

Can someone point me to the right direction

TimelordUK commented 2 years ago

I cant look at this right now but will try to do so within next day

are you able to store the iIConfig somewhere globally for now

i.e. the idea somthing like

 const sessionContainer = this.config.sessionContainer
const session = sessionContainer.resolve<FixSession>(DITokens.FixSession)

i.e in makeFactory the config object provided if you can provide this to your constructor somehow in controller then it can call resolve as above.

where session is your object created in launcher

import 'reflect-metadata'

import {
  IJsFixConfig, SessionLauncher, EngineFactory,
  ISessionDescription, SessionContainer, ISessionMsgFactory
} from 'jspurefix'
import { MDClient } from './md-client'
import { MDServer } from './md-server'
import { MsgFact } from './msg-fact'

class MySessionContainer extends SessionContainer {
  protected makeSessionFactory (description: ISessionDescription): ISessionMsgFactory {
    return new MsgFact(description)
  }
}

class AppLauncher extends SessionLauncher {
  public constructor (
    client: string = '../../data/session/test-initiator.json',
    server: string = '../../data/session/test-acceptor.json') {
    super(client, server)
    this.sessionContainer = new MySessionContainer()
    this.root = __dirname
  }

  protected override makeFactory (config: IJsFixConfig): EngineFactory {
    const isInitiator = this.isInitiator(config.description)
    return {
      makeSession: () => isInitiator ?
        new MDClient(config) :
        new MDServer(config)
    } as EngineFactory
  }
}

const l = new AppLauncher()
l.exec()
TimelordUK commented 2 years ago

or even where you have a construction of your session

 new MDClient(config)

becomes something like

const session = new MDClient(config)
const controller  = new TheController(session) // just hand in session directly 
// can controller then call send directly on session it is given?
bekee commented 2 years ago

The issue is that config object for the session is not being injected at all right after the launch exec function is called.

I placed the launcher script before the express route scripts, hence it can't inject into the controller.

TimelordUK commented 2 years ago

if you look at https://github.com/TimelordUK/jspf-md-demo/blob/master/src/app/app.ts

we see the controller created and listening bound with session

2022-06-30T20:36:00.698Z [test_server:MdController] info: start
2022-06-30T20:36:00.701Z [test_server:FixSession] info: current state Idle (24) moves to WaitingForALogon (18)
2022-06-30T20:36:00.701Z [test_server:TcpAcceptor] info: insecure connection established
2022-06-30T20:36:00.702Z [test_server:MdController] info: MdController app listening on http://localhost:3000/news
import 'reflect-metadata'

import {
  IJsFixConfig, SessionLauncher, EngineFactory,
  ISessionDescription, SessionContainer, ISessionMsgFactory, FixSession
} from 'jspurefix'
import { MDClient } from './md-client'
import { MDServer } from './md-server'
import { MsgFact } from './msg-fact'
import { MDTokens } from './md-tokens'
import { MdController } from './md-controller'

class MySessionContainer extends SessionContainer {
  protected makeSessionFactory (description: ISessionDescription): ISessionMsgFactory {
    return new MsgFact(description)
  }
}

class AppLauncher extends SessionLauncher {
  controller: MdController
  public constructor (
    client: string = '../../data/session/test-initiator.json',
    server: string = '../../data/session/test-acceptor.json') {
    super(client, server)
    this.sessionContainer = new MySessionContainer()
    this.root = __dirname
  }

  // register a custom object with the DI container.
  public makeController (c: IJsFixConfig, session: FixSession) {
    const sessionContainer = c.sessionContainer
    sessionContainer.register<MdController>(MDTokens.MDController, {
      useFactory: () => new MdController(c, session)
    })
    this.controller = sessionContainer.resolve<MdController>(MDTokens.MDController)
    this.controller.start()
  }

  stopController () {
    if (this.controller != null) {
      this.controller.stop()
    }
  }

  private makeSession (config: IJsFixConfig): FixSession {
    const server = new MDServer(config)
    this.makeController(config, server)
    return server
  }

  public launcher () {
    const instance = this
    this.run().then(() => {
      instance.stopController()
      console.log('finished.')
    }).catch(e => {
      console.error(e)
    })
  }

  protected override makeFactory (config: IJsFixConfig): EngineFactory {
    const isInitiator = this.isInitiator(config.description)
    const instance = this
    return {
      makeSession: () => isInitiator ?
        new MDClient(config) :
        instance.makeSession(config)
    } as EngineFactory
  }
}

when i refresh browser it calls controller and sends a message

2022-06-30T20:36:06.873Z [test_server:MdController] info: got a request for a news flash
2022-06-30T20:36:06.873Z [test_server:MdController] info: sending news headline mash the buy button!
2022-06-30T20:36:06.883Z [test_client:FixSession] info: B: News
2022-06-30T20:36:06.883Z [test_client:FixSession] info: [0] 8 (BeginString) = FIX4.4, [1] 9 (BodyLength) = 0000095
[2] 35 (MsgType) = B[News], [3] 49 (SenderCompID) = accept-comp
[4] 56 (TargetCompID) = init-comp, [5] 34 (MsgSeqNum) = 3
[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20220630-20:36:06.873
[8] 148 (Headline) = mash the buy button!, [9] 10 (CheckSum) = 177
2022-06-30T20:36:06.884Z [test_client:FixSession] info: ascii forwarding msgType = 'B' to application
2022-06-30T20:36:06.884Z [test_client:MDClient] info: B {
    "StandardHeader": {
        "BeginString": "FIX4.4",
        "BodyLength": 95,
        "MsgType": "B",
        "SenderCompID": "accept-comp",
        "TargetCompID": "init-comp",
        "MsgSeqNum": 3,
        "TargetSubID": "fix",
        "SendingTime": "2022-06-30T20:36:06.873Z"
    },
    "Headline": "mash the buy button!",
    "StandardTrailer": {
        "CheckSum": "177"
    }
}
2022-06-30T20:36:06.885Z [test_client:MDClient] info: mash the buy button!
bekee commented 2 years ago

Thanks @TimelordUK this approach worked pretty well, I was also able to inject the session into the express request using a middleware


    subscribe (session: TradeCaptureClient) {

      const exposeSession = async (req: any, res: Response, next: NextFunction) => {
        req.session = session;
        next();
      };

        app.use('/api', exposeSession, BaseRouter);

        app.get('/news', (req: any, res: any) => {
          this.logger.info('got a request for a news flash')
          const msg = 'mash the buy button!'

          this.logger.info(`sending news headline ${msg}`)
          session.sendNews(msg)
          res.send(msg)
        })
    }

Then called it into the controller


import 'reflect-metadata'
import { TradeCaptureClient } from "@fix/trade-capture-client"

import  { NextFunction, Request, Response } from 'express';

export class NewsController2  {

     testNews = async (req: any, res: Response, next: NextFunction) => {

        const tradingSession = req.session as TradeCaptureClient

        const headline = req.body.headline

        await tradingSession.sendNews(headline);

        return res.status(200).send("sent "+ headline)
    };
}

export default   NewsController2