@barchart/portfolio-client-js 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,273 @@
1
+ const assert = require('@barchart/common-js/lang/assert'),
2
+ Disposable = require('@barchart/common-js/lang/Disposable'),
3
+ Enum = require('@barchart/common-js/lang/Enum'),
4
+ is = require('@barchart/common-js/lang/is'),
5
+ Scheduler = require('@barchart/common-js/timing/Scheduler');
6
+
7
+ const EndpointBuilder = require('@barchart/common-js/api/http/builders/EndpointBuilder'),
8
+ Endpoint = require('@barchart/common-js/api/http/definitions/Endpoint'),
9
+ FailureReason = require('@barchart/common-js/api/failures/FailureReason'),
10
+ FailureType = require('@barchart/common-js/api/failures/FailureType'),
11
+ Gateway = require('@barchart/common-js/api/http/Gateway'),
12
+ ProtocolType = require('@barchart/common-js/api/http/definitions/ProtocolType'),
13
+ RequestInterceptor = require('@barchart/common-js/api/http/interceptors/RequestInterceptor'),
14
+ ResponseInterceptor = require('@barchart/common-js/api/http/interceptors/ResponseInterceptor'),
15
+ VerbType = require('@barchart/common-js/api/http/definitions/VerbType');
16
+
17
+ const Configuration = require('./../../common/Configuration');
18
+
19
+ module.exports = (() => {
20
+ 'use strict';
21
+
22
+ /**
23
+ * Web service gateway for translating external JWT tokens into standardized
24
+ * Barchart JWT tokens.
25
+ *
26
+ * @public
27
+ * @param {Endpoint} endpoint
28
+ * @param {Number=} refreshInterval - Interval, in milliseconds, which a token refresh should occur. If zero, the token does not need to be refreshed.
29
+ * @extends {Disposable}
30
+ */
31
+ class JwtGateway extends Disposable {
32
+ constructor(endpoint, refreshInterval) {
33
+ super();
34
+
35
+ assert.argumentIsRequired(endpoint, 'endpoint', Endpoint, 'Endpoint');
36
+ assert.argumentIsOptional(refreshInterval, 'refreshInterval', Number);
37
+
38
+ this._started = false;
39
+ this._startPromise = null;
40
+
41
+ this._endpoint = endpoint;
42
+ this._refreshInterval = refreshInterval || null;
43
+ }
44
+
45
+ /**
46
+ * Initializes the connection to the remote server and returns a promise
47
+ * containing the current instance
48
+ *
49
+ * @public
50
+ * @returns {Promise.<JwtGateway>}
51
+ */
52
+ start() {
53
+ return Promise.resolve()
54
+ .then(() => {
55
+ if (this._startPromise === null) {
56
+ this._startPromise = Promise.resolve()
57
+ .then(() => {
58
+ this._started = true;
59
+
60
+ return this;
61
+ }).catch((e) => {
62
+ this._startPromise = null;
63
+
64
+ return Promise.reject(e);
65
+ });
66
+ }
67
+
68
+ return this._startPromise;
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Retrieves a JWT token from the remote server.
74
+ *
75
+ * @public
76
+ * @returns {Promise.<String>}
77
+ */
78
+ readToken() {
79
+ return Promise.resolve()
80
+ .then(() => {
81
+ checkStart.call(this);
82
+
83
+ return Gateway.invoke(this._endpoint);
84
+ }).catch((e) => {
85
+ const failure = FailureReason.forRequest({ endpoint: this._endpoint })
86
+ .addItem(FailureType.REQUEST_IDENTITY_FAILURE)
87
+ .format();
88
+
89
+ return Promise.reject(failure);
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Returns a {@link RequestInterceptor} suitable for use with other API calls.
95
+ *
96
+ * @public
97
+ * @returns {RequestInterceptor}
98
+ */
99
+ toRequestInterceptor() {
100
+ const scheduler = new Scheduler();
101
+
102
+ let cachePromise = null;
103
+ let cacheDisposable = null;
104
+
105
+ const refreshToken = () => {
106
+ const refreshPromise = scheduler.backoff(() => this.readToken(), 100, 'Read JWT token', 3)
107
+ .then((token) => {
108
+ if (this._refreshInterval) {
109
+ cachePromise = refreshPromise;
110
+ }
111
+
112
+ if (cacheDisposable === null) {
113
+ cacheDisposable = scheduler.repeat(() => refreshToken(), this._refreshInterval, 'Refresh JWT token');
114
+ }
115
+
116
+ return token;
117
+ }).catch((e) => {
118
+ if (cacheDisposable !== null) {
119
+ cacheDisposable.dispose();
120
+
121
+ cacheDisposable = null;
122
+ cachePromise = null;
123
+ }
124
+
125
+ return Promise.reject(e);
126
+ });
127
+
128
+ return refreshPromise;
129
+ };
130
+
131
+ const delegate = (options, endpoint) => {
132
+ let tokenPromise;
133
+
134
+ if (cachePromise !== null) {
135
+ tokenPromise = cachePromise;
136
+ } else {
137
+ tokenPromise = refreshToken();
138
+ }
139
+
140
+ return tokenPromise.then((token) => {
141
+ options.headers = options.headers || { };
142
+ options.headers.Authorization = `Bearer ${token}`;
143
+
144
+ return options;
145
+ }).catch((e) => {
146
+ const failure = FailureReason.forRequest({ endpoint: endpoint })
147
+ .addItem(FailureType.REQUEST_IDENTITY_FAILURE)
148
+ .format();
149
+
150
+ return Promise.reject(failure);
151
+ });
152
+ };
153
+
154
+ return RequestInterceptor.fromDelegate(delegate);
155
+ }
156
+
157
+ /**
158
+ * Creates and starts a new {@link JwtGateway} for use in the development environment.
159
+ *
160
+ * @public
161
+ * @static
162
+ * @param {String} userId - The identifier of the user to impersonate.
163
+ * @returns {Promise.<JwtGateway>}
164
+ */
165
+ static forDevelopment(userId) {
166
+ return start(new JwtGateway(forDevelopment(userId), 60000));
167
+ }
168
+
169
+ /**
170
+ * Creates and starts a new {@link RequestInterceptor} for use in the development environment.
171
+ *
172
+ * @public
173
+ * @static
174
+ * @param {String} userId - The identifier of the user to impersonate.
175
+ * @returns {Promise.<RequestInterceptor>}
176
+ */
177
+ static forDevelopmentClient(userId) {
178
+ return JwtGateway.forDevelopment(userId)
179
+ .then((jwtGateway) => {
180
+ return jwtGateway.toRequestInterceptor();
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Creates and starts a new {@link JwtGateway} for use in the production environment.
186
+ *
187
+ * @public
188
+ * @static
189
+ * @param {RequestInterceptor} externalRequestInterceptor
190
+ * @returns {Promise.<JwtGateway>}
191
+ */
192
+ static forProduction(externalRequestInterceptor) {
193
+ assert.argumentIsRequired(externalRequestInterceptor, 'externalRequestInterceptor', RequestInterceptor, 'RequestInterceptor');
194
+
195
+ return start(new JwtGateway(forProduction(externalRequestInterceptor), 300000));
196
+ }
197
+
198
+ /**
199
+ * Creates and starts a new {@link RequestInterceptor} for use in the development environment.
200
+ *
201
+ * @public
202
+ * @static
203
+ * @param {RequestInterceptor} externalRequestInterceptor
204
+ * @returns {Promise.<RequestInterceptor>}
205
+ */
206
+ static forProductionClient(externalRequestInterceptor) {
207
+ return JwtGateway.forProduction(externalRequestInterceptor)
208
+ .then((jwtGateway) => {
209
+ return jwtGateway.toRequestInterceptor();
210
+ });
211
+ }
212
+
213
+ _onDispose() {
214
+ return;
215
+ }
216
+
217
+ toString() {
218
+ return '[JwtGateway]';
219
+ }
220
+ }
221
+
222
+ function start(gateway) {
223
+ return gateway.start()
224
+ .then(() => {
225
+ return gateway;
226
+ });
227
+ }
228
+
229
+ function checkStart() {
230
+ if (this.getIsDisposed()) {
231
+ throw new Error('Unable to use gateway, the gateway has been disposed.');
232
+ }
233
+
234
+ if (!this._started) {
235
+ throw new Error('Unable to use gateway, the gateway has not started.');
236
+ }
237
+ }
238
+
239
+ function forDevelopment(userId) {
240
+ return EndpointBuilder.for('read-jwt-token-for-development', 'lookup user identity')
241
+ .withVerb(VerbType.GET)
242
+ .withProtocol(ProtocolType.HTTPS)
243
+ .withHost(Configuration.developmentHost)
244
+ .withPathBuilder((pb) => {
245
+ pb.withLiteralParameter('token', 'token')
246
+ .withLiteralParameter('barchart', 'barchart')
247
+ .withLiteralParameter('generator', 'generator');
248
+ })
249
+ .withQueryBuilder((qb) => {
250
+ qb.withLiteralParameter('user', 'userId', userId)
251
+ .withLiteralParameter('userContext', 'userContext', 'TGAM')
252
+ .withLiteralParameter('userPermissions', 'userPermissions', 'registered');
253
+ })
254
+ .withResponseInterceptor(ResponseInterceptor.DATA)
255
+ .endpoint;
256
+ }
257
+
258
+ function forProduction(externalRequestInterceptor) {
259
+ return EndpointBuilder.for('translate-jwt-token-for-production', 'lookup Barchart user identity')
260
+ .withVerb(VerbType.GET)
261
+ .withProtocol(ProtocolType.HTTPS)
262
+ .withHost(Configuration.productionHost)
263
+ .withPathBuilder((pb) => pb.withLiteralParameter('token', 'token').withLiteralParameter('system', 'tgam').withLiteralParameter('converter', 'converter'))
264
+ .withRequestInterceptor(externalRequestInterceptor)
265
+ .withResponseInterceptor(ResponseInterceptor.DATA)
266
+ .withResponseInterceptor(ResponseInterceptor.fromDelegate((response) => {
267
+ return response.token;
268
+ }))
269
+ .endpoint;
270
+ }
271
+
272
+ return JwtGateway;
273
+ })();
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ const JwtGateway = require('./gateway/jwt/JwtGateway'),
2
+ PortfolioGateway = require('./gateway/PortfolioGateway');
3
+
4
+ module.exports = (() => {
5
+ 'use strict';
6
+
7
+ return {
8
+ JwtGateway: JwtGateway,
9
+ PortfolioGateway: PortfolioGateway,
10
+ version: '1.1.2'
11
+ };
12
+ })();
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@barchart/portfolio-client-js",
3
+ "version": "1.1.2",
4
+ "description": "JavaScript library for interfacing with Barchart's Portfolio API",
5
+ "author": {
6
+ "name": "Bryan Ingle",
7
+ "email": "bryan.ingle@barchart.com",
8
+ "url": "http://www.barchart.com"
9
+ },
10
+ "scripts": {
11
+ "test": "echo \"Error: Please use gulp to run tests\" && exit 1"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+ssh://git@github.com/barchart/barchart/watchlist-client-js.git"
16
+ },
17
+ "keywords": [
18
+ "Barchart",
19
+ "JavaScript",
20
+ "Client",
21
+ "API"
22
+ ],
23
+ "dependencies": {
24
+ "@barchart/common-js": "~3.2.0",
25
+ "@barchart/tgam-jwt-js": "~1.0.0",
26
+ "@barchart/portfolio-api-common": "~1.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "babel-core": "^6.26.0",
30
+ "babel-preset-es2015": "^6.24.1",
31
+ "babelify": "^8.0.0",
32
+ "browserify": "^14.5.0",
33
+ "git-get-status": "^1.0.5",
34
+ "glob": "^6.0.1",
35
+ "gulp": "~3.9.0",
36
+ "gulp-bump": "~1.0.0",
37
+ "gulp-git": "^2.5.1",
38
+ "gulp-jasmine": "^2.2.1",
39
+ "gulp-jsdoc3": "^1.0.1",
40
+ "gulp-jshint": "~1.11.2",
41
+ "gulp-rename": "^1.2.2",
42
+ "gulp-replace": "^0.5.4",
43
+ "gulp-util": "^3.0.7",
44
+ "jsdoc": "^3.5.5",
45
+ "run-sequence": "~1.1.4",
46
+ "vinyl-buffer": "^1.0.0",
47
+ "vinyl-source-stream": "^1.1.0",
48
+ "vinyl-transform": "^1.0.0"
49
+ },
50
+ "browserify": {
51
+ "transform": [
52
+ [
53
+ "babelify",
54
+ {
55
+ "presets": [
56
+ "es2015"
57
+ ]
58
+ }
59
+ ]
60
+ ]
61
+ },
62
+ "license": "MIT",
63
+ "bugs": {
64
+ "url": "https://github.com/barchart/watchlist-client-js/issues"
65
+ },
66
+ "homepage": "https://github.com/barchart/watchlist-client-js#readme",
67
+ "directories": {
68
+ "test": "test"
69
+ }
70
+ }
@@ -0,0 +1 @@
1
+ (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({},{},[]);