-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapi.js
More file actions
385 lines (337 loc) · 14.1 KB
/
api.js
File metadata and controls
385 lines (337 loc) · 14.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
const Asset = require("./common/asset");
const AssetProperties = require('./common/assetproperties');
const Report = require("./common/report");
const Tools = require("./common/tools");
const User = require("./common/user");
const Util = require("./common/util");
const Workflow = require("./common/workflow");
const apiErrors = {
InstanceNameInvalid: 1
}
/********************************************
* Javascript API for utilizing the crownpeak API
* All function are asyncronous and are operated upon by
* promises
*
********************************************/
class api {
constructor() {
this.host;
this.instance;
this.apiKey;
this._isAuthenticated = false;
this._error;
this.cookie;
this.https = require("https");
this.webAPIRoot = "/cpt_webservice/accessapi/";
this.Asset = new Asset(this);
this.AssetProperties = new AssetProperties(this);
this.Report = new Report(this);
this.User = new User(this);
this.Tools = new Tools(this);
this.Workflow = new Workflow(this);
this.setCookie = require('set-cookie-parser');
this.fetch = require("node-fetch");
}
get isAuthenticated() {
return this._isAuthenticated;
}
get error() {
return this._error;
}
/**
* Authenticate the user such that all other function can run
* @param {string} username - The username of the user
* @param {string} password - The password of the user
* @param {string} host - The host of the user
* @param {string} instance
* @param {string} apiKey
*/
/**
* Pre-populate the helper with a session captured externally (e.g. from a
* browser-driven login). Skips the password POST entirely. Subsequent calls
* use the supplied cookie and x-api-key the same way they would after a
* normal login().
* @param {object} session
* @param {string} session.host
* @param {string} session.instance
* @param {string} session.apiKey
* @param {Array<string>} session.cookie Array of "name=value; Path=/; ..." strings,
* same shape as set-cookie-parser.splitCookiesString output.
*/
setSession({ host, instance, apiKey, cookie }) {
if (!host || !instance || !apiKey || !cookie) {
throw new Error("setSession requires host, instance, apiKey, and cookie");
}
this.host = host;
this.instance = instance;
this.apiKey = apiKey;
this.cookie = cookie;
this._isAuthenticated = true;
}
clearSession() {
this._isAuthenticated = false;
this.cookie = undefined;
}
async login(username, password, host, instance, apiKey) {
this.host = host;
this.instance = instance;
this.apiKey = apiKey;
var apiParent = this;
var parentResponse; //Store the response for return of the await
var parentError; //Store the error
var authJson = {
"instance": instance,
"username": username,
"password": password,
"remember_me": "false",
"timeZoneOffsetMinutes": "-480"
};
await this.postRequest("/Auth/Authenticate", authJson,
function(response) {
parentResponse = response;
},
function(error) {
parentError = error;
});
if (parentResponse !== undefined) {
var jsonBody = parentResponse;
if (jsonBody.resultCode === Util.ResponseMessages.Success) {
//User was successfully authenticated
this._isAuthenticated = true;
this.cookie = parentResponse.cookie;
} else {
this._error = parentResponse;
}
} else {
if (parentError !== undefined && Util.IsValidJSONString(parentError.error)) {
this._error = parentError;
throw parentError;
}
throw Error("Unable to contact CMS, please check your settings");
}
return parentResponse;
}
static currentDate() {
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
return mm + '/' + dd + '/' + yyyy;
}
/**
*
* @param {string} urlPath - The path to call from the url
* @param {JSON|string} data - The data to send in the request
* @param {function(object)} callback - function to run on succes
* @param {function(object)} onError - function to run on error
*/
async postRequest(urlPath, data, callback, onError) {
var currentAPI = this;
var attempt = 0;
if (urlPath.startsWith("/")) {
urlPath = urlPath.substring(1, urlPath.length);
}
const options = {
url: "https://" + this.host + "/" + this.instance + this.webAPIRoot + urlPath, //URL of the instance
headers: {
'x-api-key': this.apiKey, //The API Key
'accept': 'application/json', //Data is returned in JSON format
'Content-Type': 'text/json', //Data is sent in JSON format
'cp-datetime': api.currentDate() //The current datetime
},
body: JSON.stringify(data),
//proxy: "http://127.0.0.1:8888",
resolveWithFullResponse: true,
method: "POST"
}
if (this._isAuthenticated) {
// Format as a proper Cookie request header: "name=value; name=value".
// Assigning the raw array would let node-fetch comma-join the elements, which corrupts
// every cookie after the first when the response had multiple Set-Cookie headers — the
// server-side parser splits on ";" and ends up with names like "Secure,ASPSESSIONID".
// Matches what getCmsRequest already does.
options.headers.cookie = this.cookie.map(c => c.split(";")[0]).join("; ");
}
try {
const response = await this.fetch("https://" + this.host + "/" + this.instance + this.webAPIRoot + urlPath, options);
const data = await response.json();
if (data.resultCode === Util.ResponseMessages.Success) {
var combinedCookieHeader = response.headers.get('set-cookie');
data.cookie = this.setCookie.splitCookiesString(combinedCookieHeader);
}
callback(data);
} catch (error) {
var timeoutWait = Util.InitialTimeoutWait;
if (error !== undefined && error["statusCode"] == Util.StatusCode.Timeout && attempt < 3) {
attempt++;
/*If error is a timeout, then retry either after the given retry amount in Retry-After isn't set,
otherwise use the time given */
if (error.response !== undefined && error.response.headers !== undefined && error.response.headers["retry-after"] !== undefined) {
var retryAfter = parseInt(error.response.headers["retry-after"], 10);
if (retryAfter != "NaN") {
timeoutWait = retryAfter * 1000;
}
}
await Util.timeout(timeoutWait);
await currentAPI.postRequest(urlPath, data, callback, onError);
} else {
if (onError !== undefined) {
onError(error);
}
}
}
}
/**
*
* @param {string} urlPath - The path to call from the url
* @param {function(object)} callback - function to run on success
* @param {function(object)=} onError - function to run on error
*/
async getCmsRequest(urlPath, callback, onError) {
var currentAPI = this;
var attempt = 0;
if (urlPath.startsWith("/")) {
urlPath = urlPath.substring(1, urlPath.length);
}
const options = {
url: "https://" + this.host + "/" + urlPath, //URL of the instance
headers: {},
resolveWithFullResponse: true,
method: "GET",
encoding: null
}
if (this._isAuthenticated) {
options.headers.cookie = this.cookie.map(c => c.split(";")[0]).join("; "); // this.cookie;
}
try {
const response = await this.fetch("https://" + this.host + "/" + urlPath, options);
const data = await response.json();
callback(data);
} catch (error) {
var timeoutWait = Util.InitialTimeoutWait;
if (error !== undefined && error["statusCode"] == Util.StatusCode.Timeout && attempt < 3) {
attempt++;
/*If error is a timeout, then retry either after the given retry amount in Retry-After isn't set,
otherwise use the time given */
if (error.response !== undefined && error.response.headers !== undefined && error.response.headers["retry-after"] !== undefined) {
var retryAfter = parseInt(error.response.headers["retry-after"], 10);
if (retryAfter != "NaN") {
timeoutWait = retryAfter * 1000;
}
}
await Util.timeout(timeoutWait);
await currentAPI.getCmsRequest(urlPath, callback, onError);
} else {
if (onError !== undefined) {
onError(error);
}
}
}
}
/**
*
* @param {string} urlPath - The path to call from the url
* @param {function(object)} callback - function to run on success
* @param {function(object)=} onError - function to run on error
*/
async getCmsRequestRaw(urlPath, callback, onError) {
var currentAPI = this;
var attempt = 0;
if (urlPath.startsWith("/")) {
urlPath = urlPath.substring(1, urlPath.length);
}
const options = {
url: "https://" + this.host + "/" + urlPath, //URL of the instance
headers: {},
resolveWithFullResponse: true,
method: "GET",
encoding: null
}
if (this._isAuthenticated) {
options.headers.cookie = this.cookie.map(c => c.split(";")[0]).join("; "); // this.cookie;
}
try {
const response = await this.fetch("https://" + this.host + "/" + urlPath, options);
callback(response);
} catch (error) {
var timeoutWait = Util.InitialTimeoutWait;
if (error !== undefined && error["statusCode"] == Util.StatusCode.Timeout && attempt < 3) {
attempt++;
/*If error is a timeout, then retry either after the given retry amount in Retry-After isn't set,
otherwise use the time given */
if (error.response !== undefined && error.response.headers !== undefined && error.response.headers["retry-after"] !== undefined) {
var retryAfter = parseInt(error.response.headers["retry-after"], 10);
if (retryAfter != "NaN") {
timeoutWait = retryAfter * 1000;
}
}
await Util.timeout(timeoutWait);
await currentAPI.getCmsRequest(urlPath, callback, onError);
} else {
if (onError !== undefined) {
onError(error);
}
}
}
}
/**
*
* @param {string} urlPath - The path to call from the url
* @param {JSON|string} data - The data to send in the request
* @param {function(object)} callback - function to run on succes
* @param {function(object)} onError - function to run on error
*/
postRequestV1(urlPath, data, callback, onError) {
var currentAPI = this;
var attempt = 0;
if (urlPath.startsWith("/")) {
urlPath = urlPath.substring(1, urlPath.length);
}
const options = {
url: "https://" + this.host + "/" + this.instance + this.webAPIRoot + urlPath, //URL of the instance
headers: {
'x-api-key': this.apiKey, //The API Key
'accept': 'application/json', //Data is returned in JSON format
'Content-Type': 'text/json', //Data is sent in JSON format
'cp-datetime': api.currentDate() //The current datetime
},
body: JSON.stringify(data),
//proxy: "http://127.0.0.1:8888",
resolveWithFullResponse: true,
method: "POST"
}
if (this._isAuthenticated) {
options.headers.cookie = this.cookie;
}
return this.requestLib.post(options).then(callback).catch(
function(error) {
var timeoutWait = Util.InitialTimeoutWait;
if (error !== undefined && error["statusCode"] == Util.StatusCode.Timeout && attempt < 3) {
attempt++;
/*If error is a timeout, then retry either after the given retry amount in Retry-After isn't set,
otherwise use the time given */
if (error.response !== undefined && error.response.headers !== undefined && error.response.headers["retry-after"] !== undefined) {
var retryAfter = parseInt(error.response.headers["retry-after"], 10);
if (retryAfter != "NaN") {
timeoutWait = retryAfter * 1000;
}
}
setTimeout(currentAPI.postRequest(urlPath, data, callback, onError), timeoutWait);
} else {
if (onError !== undefined) {
onError(error);
}
}
}
);
}
Asset = Asset;
AssetProperties = AssetProperties;
Report = Report;
Tools = Tools;
User = User;
Util = Util;
Workflow = Workflow;
}
module.exports = api;