From 0061a269b32000df3e81280346207a22eb12b15b Mon Sep 17 00:00:00 2001 From: Bryan Ingle Date: Mon, 14 Oct 2024 15:21:31 -0600 Subject: [PATCH] Release. Bump version number --- docs/_coverpage.md | 2 +- example/browser/example.js | 177 ++++++++++++++++++++++++++++++------- lib/meta.js | 2 +- package.json | 2 +- 4 files changed, 147 insertions(+), 36 deletions(-) diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 0b654c0..ec2ca4e 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,4 +1,4 @@ -# Barchart Market Data SDK JavaScript 6.2.6 +# Barchart Market Data SDK JavaScript 6.3.0 > Inject real-time market data into your JavaScript applications diff --git a/example/browser/example.js b/example/browser/example.js index 7b133ed..79d50ce 100644 --- a/example/browser/example.js +++ b/example/browser/example.js @@ -453,6 +453,7 @@ module.exports = (() => { },{"./../../../lib/connection/Connection":2,"./../../../lib/marketState/Profile":20,"./../../../lib/meta":22,"./../../../lib/utilities/data/AssetClass":27,"./../../../lib/utilities/data/timezones":29,"./../../../lib/utilities/format/decimal":31,"./../../../lib/utilities/format/price":34,"./../../../lib/utilities/format/quote":35,"./../../../lib/utilities/format/specialized/cmdtyView":36}],2:[function(require,module,exports){ const array = require('@barchart/common-js/lang/array'), + assert = require('@barchart/common-js/lang/assert'), object = require('@barchart/common-js/lang/object'); const ConnectionBase = require('./ConnectionBase'), parseMessage = require('./../utilities/parse/ddf/message'); @@ -468,6 +469,10 @@ module.exports = (() => { 'use strict'; const _API_VERSION = 4; + const mode = { + credentials: 'credentials', + token: 'token' + }; const state = { connecting: 'CONNECTING', authenticating: 'LOGGING_IN', @@ -499,6 +504,8 @@ module.exports = (() => { }; const _RECONNECT_INTERVAL = 5000; const _WATCHDOG_INTERVAL = 10000; + const regex = {}; + regex.hostname = /^(?:(wss|ws):\/\/)?(.+?)(?::(\d+))?$/i; function ConnectionInternal(marketState, instance) { const __logger = LoggerFactory.getLogger('@barchart/marketdata-api-js'); const __instance = instance; @@ -508,6 +515,7 @@ module.exports = (() => { let __xmlParser = null; let __connection = null; let __connectionState = state.disconnected; + let __connectionCount = 0; let __paused = false; let __reconnectAllowed = false; let __pollingFrequency = null; @@ -532,9 +540,12 @@ module.exports = (() => { timestamp: [] }; const __loginInfo = { + mode: null, hostname: null, username: null, - password: null + password: null, + jwtProvider: null, + jwtPromise: null }; let __decoder = null; let __diagnosticsController = null; @@ -585,16 +596,17 @@ module.exports = (() => { * * @private * @param {String} hostname - * @param {String} username - * @param {String} password + * @param {String=} username + * @param {String=} password * @param {WebSocketAdapterFactory} webSocketAdapterFactory * @param {XmlParserFactory} xmlParserFactory + * @param {Callbacks.JwtProvider} jwtProvider */ - function initializeConnection(hostname, username, password, webSocketAdapterFactory, xmlParserFactory) { + function initializeConnection(hostname, username, password, webSocketAdapterFactory, xmlParserFactory, jwtProvider) { __connectionFactory = webSocketAdapterFactory; __xmlParserFactory = xmlParserFactory; __reconnectAllowed = true; - connect(hostname, username, password); + connect(hostname, username, password, jwtProvider); } /** @@ -608,9 +620,12 @@ module.exports = (() => { event: 'disconnecting' }); __reconnectAllowed = false; + __loginInfo.mode = null; + __loginInfo.hostname = null; __loginInfo.username = null; __loginInfo.password = null; - __loginInfo.hostname = null; + __loginInfo.jwtProvider = null; + __loginInfo.jwtPromise = null; __knownConsumerSymbols = {}; __pendingProfileSymbols = {}; __listeners.marketDepth = {}; @@ -622,22 +637,52 @@ module.exports = (() => { disconnect(); } + /** + * Attempts to read a JWT from an external provider. + * + * @private + * @param {Callbacks.JwtProvider} jwtProvider + * @returns {Promise} + */ + function getJwt(jwtProvider) { + const connectionCount = __connectionCount; + return Promise.resolve().then(() => { + __logger.log(`Connection [ ${__instance} ]: Requesting JWT for connection attempt [ ${connectionCount} ].`); + return jwtProvider(); + }).then(jwt => { + __logger.log(`Connection [ ${__instance} ]: Request for JWT was successful for connection attempt [ ${connectionCount} ].`); + if (__connectionCount !== connectionCount) { + return null; + } + if (typeof jwt !== 'string') { + __logger.warn(`Connection [ ${__instance} ]: Unable to extract JWT.`); + return null; + } + return jwt; + }).catch(e => { + __logger.warn(`Connection [ ${__instance} ]: Request for JWT failed for connection attempt [ ${connectionCount} ].`); + return null; + }); + } + /** * Attempts to establish a connection to JERQ. * * @private * @param {String} hostname - * @param {String} username - * @param {String} password - */ - function connect(hostname, username, password) { - if (!hostname) { - throw new Error('Unable to connect, the "hostname" argument is required.'); - } - if (!username) { + * @param {String=} username + * @param {String=} password + * @param {Callbacks.JwtProvider=} jwtProvider + */ + function connect(hostname, username, password, jwtProvider) { + assert.argumentIsRequired(hostname, 'hostname', String); + assert.argumentIsOptional(username, 'username', String); + assert.argumentIsOptional(password, 'password', String); + assert.argumentIsOptional(jwtProvider, 'jwtProvider', Function); + if (!username && !jwtProvider) { throw new Error('Unable to connect, the "username" argument is required.'); } - if (!password) { + if (!password && !jwtProvider) { throw new Error('Unable to connect, the "password" argument is required.'); } if (__connection !== null) { @@ -645,22 +690,51 @@ module.exports = (() => { return; } ensureExchangeMetadata(); - __logger.log(`Connection [ ${__instance} ]: Initializing. Version [ ${version} ]. Using [ ${username}@${hostname} ].`); - __xmlParser = __xmlParserFactory.build(); + __connectionCount = __connectionCount + 1; + __logger.log(`Connection [ ${__instance} ]: Starting connection attempt [ ${__connectionCount} ] using [ ${jwtProvider ? 'JWT' : 'credentials-based'} ] authentication.`); let protocol; + let host; let port; if (hostname === 'localhost') { protocol = 'ws'; + host = 'localhost'; port = 8080; } else { - protocol = 'wss'; - port = 443; + const match = hostname.match(regex.hostname); + if (match !== null && match[1]) { + protocol = match[1]; + } else { + protocol = 'wss'; + } + if (match !== null && match[2]) { + host = match[2]; + } else { + host = hostname; + } + if (match !== null && match[3]) { + port = parseInt(match[3]); + } else { + port = protocol === 'ws' ? 80 : 443; + } } - __loginInfo.username = username; - __loginInfo.password = password; __loginInfo.hostname = hostname; + __loginInfo.username = null; + __loginInfo.password = null; + __loginInfo.jwtProvider = null; + __loginInfo.jwtPromise = null; + if (jwtProvider) { + __loginInfo.mode = mode.token; + __loginInfo.jwtProvider = jwtProvider; + __loginInfo.jwtPromise = getJwt(jwtProvider); + } else { + __loginInfo.mode = mode.credentials; + __loginInfo.username = username; + __loginInfo.password = password; + } + __xmlParser = __xmlParserFactory.build(); __connectionState = state.disconnected; - __connection = __connectionFactory.build(`${protocol}://${__loginInfo.hostname}:${port}/jerq`); + __logger.log(`Connection [ ${__instance} ]: Opening connection to [ ${protocol}://${host}:${port} ].`); + __connection = __connectionFactory.build(`${protocol}://${host}:${port}/jerq`); __connection.binaryType = 'arraybuffer'; __decoder = __connection.getDecoder(); __connection.onopen = () => { @@ -708,7 +782,7 @@ module.exports = (() => { } if (__reconnectAllowed) { __logger.log(`Connection [ ${__instance} ]: Scheduling reconnect attempt.`); - const reconnectAction = () => connect(__loginInfo.hostname, __loginInfo.username, __loginInfo.password); + const reconnectAction = () => connect(__loginInfo.hostname, __loginInfo.username, __loginInfo.password, __loginInfo.jwtProvider); const reconnectDelay = _RECONNECT_INTERVAL + Math.floor(Math.random() * _WATCHDOG_INTERVAL); setTimeout(reconnectAction, reconnectDelay); } @@ -1145,8 +1219,31 @@ module.exports = (() => { } if (lines.some(line => line === '+++')) { __connectionState = state.authenticating; - __logger.log(`Connection [ ${__instance} ]: Sending credentials.`); - __connection.send(`LOGIN ${__loginInfo.username}:${__loginInfo.password} VERSION=${_API_VERSION}\r\n`); + let connectionCount = __connectionCount; + if (__loginInfo.mode === mode.credentials) { + __connection.send(`LOGIN ${__loginInfo.username}:${__loginInfo.password} VERSION=${_API_VERSION}\r\n`); + return; + } + if (__loginInfo.mode === mode.token) { + const jwtPromise = __loginInfo.jwtPromise || Promise.resolve(null); + jwtPromise.then(jwt => { + if (__connectionCount !== connectionCount) { + return; + } + if (__connectionState !== state.authenticating) { + return; + } + if (jwt === null) { + broadcastEvent('events', { + event: 'jwt acquisition failed' + }); + disconnect(); + return; + } + __connection.send(`TOKEN ${jwt} VERSION=${_API_VERSION}\r\n`); + }); + return; + } } } else if (__connectionState === state.authenticating) { const lines = message.split('\n'); @@ -2028,7 +2125,7 @@ module.exports = (() => { this._internal = ConnectionInternal(this.getMarketState(), this._getInstance()); } _connect(webSocketAdapterFactory, xmlParserFactory) { - this._internal.connect(this.getHostname(), this.getUsername(), this.getPassword(), webSocketAdapterFactory, xmlParserFactory); + this._internal.connect(this.getHostname(), this.getUsername(), this.getPassword(), webSocketAdapterFactory, xmlParserFactory, this.getJwtProvider()); } _disconnect() { this._internal.disconnect(); @@ -2070,7 +2167,7 @@ module.exports = (() => { return Connection; })(); -},{"./../logging/LoggerFactory":15,"./../meta":22,"./../utilities/parse/ddf/message":38,"./../utilities/parsers/SymbolParser":41,"./ConnectionBase":3,"./diagnostics/DiagnosticsControllerBase":7,"./snapshots/exchanges/retrieveExchanges":8,"./snapshots/profiles/retrieveExtensions":9,"./snapshots/quotes/retrieveExtensions":10,"./snapshots/quotes/retrieveSnapshots":11,"@barchart/common-js/lang/array":51,"@barchart/common-js/lang/object":54}],3:[function(require,module,exports){ +},{"./../logging/LoggerFactory":15,"./../meta":22,"./../utilities/parse/ddf/message":38,"./../utilities/parsers/SymbolParser":41,"./ConnectionBase":3,"./diagnostics/DiagnosticsControllerBase":7,"./snapshots/exchanges/retrieveExchanges":8,"./snapshots/profiles/retrieveExtensions":9,"./snapshots/quotes/retrieveExtensions":10,"./snapshots/quotes/retrieveSnapshots":11,"@barchart/common-js/lang/array":51,"@barchart/common-js/lang/assert":52,"@barchart/common-js/lang/object":54}],3:[function(require,module,exports){ const is = require('@barchart/common-js/lang/is'); const Environment = require('./../environment/Environment'), EnvironmentForBrowsers = require('./../environment/EnvironmentForBrowsers'); @@ -2094,6 +2191,7 @@ module.exports = (() => { this._hostname = null; this._username = null; this._password = null; + this._jwtProvider = null; this._environment = environment || new EnvironmentForBrowsers(); this._marketState = new MarketState(symbol => this._handleProfileRequest(symbol)); this._pollingFrequency = null; @@ -2110,15 +2208,17 @@ module.exports = (() => { * * @public * @param {string} hostname - Barchart hostname (contact solutions@barchart.com) - * @param {string} username - Your username (contact solutions@barchart.com) - * @param {string} password - Your password (contact solutions@barchart.com) + * @param {string=} username - Your username (contact solutions@barchart.com) + * @param {string=} password - Your password (contact solutions@barchart.com) * @param {WebSocketAdapterFactory=} webSocketAdapterFactory - Strategy for creating a {@link WebSocketAdapterFactory} instances (overrides {@link Environment} settings). * @param {XmlParserFactory=} xmlParserFactory - Strategy for creating a {@link WebSocketAdapterFactory} instances (overrides {@link Environment} settings). + * @param {Callbacks.JwtProvider=} jwtProvider - A function which returns a JWT (or a promise for a JWT) that is used as an alternative for actual credentials. */ - connect(hostname, username, password, webSocketAdapterFactory, xmlParserFactory) { + connect(hostname, username, password, webSocketAdapterFactory, xmlParserFactory, jwtProvider) { this._hostname = hostname; - this._username = username; - this._password = password; + this._username = username || null; + this._password = password || null; + this._jwtProvider = jwtProvider || null; this._connect(webSocketAdapterFactory || this._environment.getWebSocketAdapterFactory(), xmlParserFactory || this._environment.getXmlParserFactory()); } @@ -2142,6 +2242,7 @@ module.exports = (() => { this._hostname = null; this._username = null; this._password = null; + this._jwtProvider = null; this._disconnect(); } @@ -2428,6 +2529,16 @@ module.exports = (() => { return this._username; } + /** + * The username used to authenticate to Barchart. + * + * @public + * @returns {null|Callbacks.JwtProvider} + */ + getJwtProvider() { + return this._jwtProvider; + } + /** * Gets a unique identifier for the current instance. * @@ -5309,7 +5420,7 @@ module.exports = (() => { 'use strict'; return { - version: '6.2.6' + version: '6.3.0' }; })(); diff --git a/lib/meta.js b/lib/meta.js index b15f80d..2a9607f 100644 --- a/lib/meta.js +++ b/lib/meta.js @@ -2,6 +2,6 @@ module.exports = (() => { 'use strict'; return { - version: '6.2.6' + version: '6.3.0' }; })(); \ No newline at end of file diff --git a/package.json b/package.json index 09472ff..b7015fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@barchart/marketdata-api-js", - "version": "6.2.6", + "version": "6.3.0", "description": "SDK for streaming market data from Barchart.com", "author": { "name": "Eero Pikat",