8000 First commit with basic functionality · DHLRPAadmin/orchestrator-nodejs@0a56f25 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0a56f25

Browse files
committed
First commit with basic functionality
0 parents  commit 0a56f25

File tree

7 files changed

+550
-0
lines changed

7 files changed

+550
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./lib/Orchestrator');

lib/Orchestrator.js

Lines changed: 296 additions & 0 deletions
10AFC
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
'use strict';
2+
3+
// native
4+
var querystring = require('querystring');
5+
6+
// local
7+
var restHelper = require('./restHelper');
8+
9+
var WELL_KNOWN_METHODS = restHelper.WELL_KNOWN_METHODS;
10+
11+
// TODO we may want to handle relative path for servers configured to host Orchestrator other than at root
12+
13+
/**
14+
* @param {OrchestratorOptions} options
15+
* @returns {OrchestratorOptions}
16+
*/
17+
function validateOptions(options) {
18+
if (!options) {
19+
throw new Error('OrchestratorOptions cannot be undefined');
20+
}
21+
// Default to secure connection
22+
if (options.isSecure !== true || options.isSecure !== false) {
23+
options.isSecure = true;
24+
}
25+
return options;
26+
}
27+
28+
/**
29+
* @param {OrchestratorOptions} options
30+
* @constructor
31+
*/
32+
function Orchestrator(options) {
33+
/**
34+
* @type {OrchestratorOptions}
35+
* @private
36+
*/
37+
this._options = validateOptions(options);
38+
/**
39+
* @type {Array.<OrchestratorQueueElement>}
40+
* @private
41+
*/
42+
this._queue = [];
43+
/**
44+
* @type {boolean}
45+
* @private
46+
*/
47+
this._processing = false;
48+
/**
49+
* @type {string|undefined}
50+
* @private
51+
*/
52+
this._credentials = undefined;
53+
}
54+
55+
module.exports = Orchestrator;
56+
57+
/**
58+
* @private
59+
* @param {function(Error=)} callback
60+
*/
61+
Orchestrator.prototype._login = function (callback) {
62+
var self = this;
63+
/** @type {OrchestratorOptions} */
64+
var options = this._options;
65+
/** @type {OrchestratorRestHelperOptions} */
66+
var requestOptions = {
67+
hostname: options.hostname,
68+
isSecure: options.isSecure,
69+
port: options.port,
70+
path: '/api/Account'
71+
};
72+
/** @type {Object} */
73+
var loginRequestData = {
74+
'TenancyName': options.tenancyName,
75+
'UsernameOrEmailAddress': options.usernameOrEmailAddress,
76+
'Password': options.password
77+
};
78+
restHelper.postJSON(
79+
requestOptions,
80+
loginRequestData,
81+
/**
82+
* @param {Error} err
83+
* @param {OrchestratorLoginResponse} response
84+
*/
85+
function (err, response) {
86+
if (!err) {
87+
self._credentials = response.result;
88+
}
89+
callback(err);
90+
}
91+
);
92+
};
93+
94+
/**
95+
* @param {OrchestratorRestHelperCallback} callback
96+
* @param {Error} err
97+
* @param {Object} [data]
98+
* @private
99+
*/
100+
Orchestrator.prototype._triggerQueueElementCallback = function (callback, err, data) {
101+
// Use a try-finally to make sure the application code does not interrupt the queue
102+
try {
103+
callback(err, data);
104+
} finally {
105+
this._processing = false;
106+
this._processQueue();
107+
}
108+
};
109+
110+
/**
111+
* @param {OrchestratorQueueElement} element
112+
* @param {boolean} isRetry
113+
* @returns {OrchestratorRestHelperCallback}
114+
*/
115+
Orchestrator.prototype._processingResultHandlerFactory = function (element, isRetry) {
116+
var self = this;
117+
return function (err, data) {
118+
if (err && !isRetry) {
119+
self._login(function (err) { // attempt to login before retrying
120+
if (err) {
121+
self._credentials = undefined; // login failed so reset credentials
122+
self._triggerQueueElementCallback(element.cb, err);
123+
return;
124+
}
125+
// retry with new credentials
126+
self._processElement(element, true);
127+
});
128+
return;
129+
}
130 10000 +
// succeed or fail, but no retry
131+
self._triggerQueueElementCallback(element.cb, err, data);
132+
};
133+
};
134+
135+
/**
136+
* @param {OrchestratorQueueElement} element
137+
* @param {boolean} isRetry
138+
* @private
139+
*/
140+
Orchestrator.prototype._processElement = function (element, isRetry) {
141+
/** @type {OrchestratorRestHelperOptions} */
142+
var options = element.options;
143+
144+
options.headers = options.headers || {};
145+
options.headers.authorization = 'bearer ' + this._credentials; // TODO check if systematically needed
146+
switch (element.method) {
147+
case WELL_KNOWN_METHODS.GET:
148+
restHelper.getJSON(
149+
element.options,
150+
this._processingResultHandlerFactory(element, isRetry)
151+
);
152+
break;
153+
case WELL_KNOWN_METHODS.POST:
154+
restHelper.postJSON(
155+
element.options,
156+
element.data,
157+
this._processingResultHandlerFactory(element, isRetry)
158+
);
159+
break;
160+
case WELL_KNOWN_METHODS.PUT:
161+
restHelper.putJSON(
162+
element.options,
163+
element.data,
164+
this._processingResultHandlerFactory(element, isRetry)
165+
);
166+
break;
167+
default:
168+
element.cb(new Error('Method not supported: ' + element.method));
169+
}
170+
};
171+
172+
/** @private */
173+
Orchestrator.prototype._processQueue = function () {
174+
var self = this;
175+
/** @type {OrchestratorQueueElement|undefined} */
176+
var element;
177+
178+
if (this._processing) { // already processing an element
179+
return;
180+
}
181+
element = this._queue.shift();
182+
if (element === undefined) { // nothing left to process
183+
return;
184+
}
185+
this._processing = true;
186+
if (this._credentials) { // already logged-in, move on to processing the element
187+
this._processElement(element, false); // credentials may be expired
188+
return;
189+
}
190+
this._login(function (err) { // attempt to login before processing
191+
if (err) {
192+
self._triggerQueueElementCallback(element.cb, err);
193+
return;
194+
}
195+
self._processElement(element, true); // credentials are expected to work
196+
});
197+
};
198+
199+
/**
200+
* @param {string} path
201+
* @param {Object.<string|Array.<string>>} query
202+
* @param {function(err: Error|undefined, response: Object=)} callback
203+
*/
204+
Orchestrator.prototype.get = function (path, query, callback) {
205+
/** @type {OrchestratorOptions} */
206+
var options = this._options;
207+
/** @type {string} */
208+
var pathWithQuery = path + '?' + querystring.encode(query);
209+
210+
this._queue.push({
211+
method: WELL_KNOWN_METHODS.GET,
212+
options: {
213+
hostname: options.hostname,
214+
isSecure: options.isSecure,
215+
port: options.port,
216+
path: pathWithQuery
217+
},
218+
cb: callback
219+
});
220+
this._processQueue();
221+
};
222+
223+
/**
224+
* @param {string} path
225+
* @param {Object} data
226+
* @param {function(err: Error|undefined, response: Object=)} callback
227+
*/
228+
Orchestrator.prototype.post = function (path, data, callback) {
229+
/** @type {OrchestratorOptions} */
230+
var options = this._options;
231+
232+
this._queue.push({
233+
method: WELL_KNOWN_METHODS.POST,
234+
options: {
235+
hostname: options.hostname,
236+
isSecure: options.isSecure,
237+
port: options.port,
238+
path: path
239+
},
240+
cb: callback
241+
});
242+
this._processQueue();
243+
};
244+
245+
/**
246+
* @param {string} path
247+
* @param {Object} data
248+
* @param {function(err: Error|undefined, response: Object=)} callback
249+
*/
250+
Orchestrator.prototype.put = function (path, data, callback) {
251+
/** @type {OrchestratorOptions} */
252+
var options = this._options;
253+
254+
this._queue.push({
255+
method: WELL_KNOWN_METHODS.PUT,
256+
options: {
257+
hostname: options.hostname,
258+
isSecure: options.isSecure,
259+
port: options.port,
260+
path: path
261+
},
262+
cb: callback
263+
});
264+
this._processQueue();
265+
};
266+
267+
/**
268+
* @typedef {Object} OrchestratorOptions
269+
* @property {string} tenancyName
270+
* @property {string} usernameOrEmailAddress
271+
* @property {string} password
272+
* @property {string} hostname
273+
* @property {boolean} isSecure
274+
* @property {number|undefined} port
275+
*/
276+
277+
/**
278+
* @typedef {Object} OrchestratorResponseBase
279+
* @property {string|null} targetUrl
280+
* @property {boolean} success
281+
* @property {string|null} error
282+
* @property {boolean} unAuthorizedRequest
283+
*/
284+
285+
/**
286+
* @typedef {OrchestratorResponseBase} OrchestratorLoginResponse
287+
* @property {string} result - The token value
288+
*/
289+
290+
/**
291+
* @typedef {Object} OrchestratorQueueElement
292+
* @property {WELL_KNOWN_METHODS} method
293+
* @property {OrchestratorRestHelperOptions} options
294+
* @property {Object|undefined} data
295+
* @property {OrchestratorRestHelperCallback} cb
296+
*/

0 commit comments

Comments
 (0)
0