This repository was archived by the owner on Feb 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathapp.js
More file actions
258 lines (246 loc) · 9.88 KB
/
app.js
File metadata and controls
258 lines (246 loc) · 9.88 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
//
// Change these to your server's url (and port if not 80) and the site you wish to use it on.
//
var SERVERURL = "10.211.55.11";
var SITE = "rest";
var ADMIN_USER_NAME = "admin";
var ADMIN_PW = "admin";
//Loading of Module Dependencies
var XMLWriter = require('xml-writer');
var request = require("request");
var express = require("express");
var jsxml = require("node-jsxml");
var app = express();
//Express middleware set up
//Express is a web framework for NodeJS. This is what we're using to act as a web server
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.use(express.urlencoded());
app.use(express.static(__dirname + '/public'));
app.use(express.cookieParser('Ronaldinho'));
app.use(express.session());
// When we query all of the users on the site, we will store them globally so that other pages can use them.
var userIDs = {};
//Routes
// This is how we tell the Express what to do and which page to load when the user navigates to different pages.
app.get('/', function(req,res) {
// The first step to using the REST API is to log in. You can log in as an admin or a non-admin, but
// Many of the functions only apply to admins. Logging in returns an auth token which must be placed in the header
// of all future calls.
//
// In this case we decide to get the token as soon as a user starts using our app. This happens transparently to the user,
// but when this process begins depends on the design of your application/script.
// If we don't have a login token (ie if this is the beginning of the session)
// then we make a post to /api/2.0/auth/signin to login as an admin and store the
// login token as part of the session cookie.
if(!req.session.authToken) {
//Build the XML payload
// This will happen differently in different programming languages and depending on what library/module you
// decide to do it with. Here I'm using a module called xml writer which simplifies the process.
// To see what xml you need to build for each of the different calls, see the documentation.
var reqxml = new XMLWriter();
reqxml.startElement('tsRequest').startElement('credentials').writeAttribute('name', ADMIN_USER_NAME)
.writeAttribute('password', ADMIN_PW).startElement('site').writeAttribute('contentUrl', '');
request.post(
{
url: 'http://' + SERVERURL + '/api/2.0/auth/signin',
body: reqxml.toString(),
headers: {'Content-Type': 'text/xml'}
},
// Express requests take a 'callback' function which will be called when the request has been processed. The
// response from the server will be contained in the 3rd parameter 'body'.
function(err, response, body) {
if(err) {
req.session.err = err;
} else {
// In order to grab information from the response, we turn it into an xml object and use a module
// called node-jsxml to parse the xml. node-jsxml allows us to use child(), attribute(), and some other functions
// to locate specific elements and pieces of information that we need.
// Here, we need to grab the 'token' attribute and store it in the session cookie.
var bodyXML = new jsxml.XML(body);
req.session.authToken = bodyXML.child('credentials').attribute("token").getValue();
console.log("Auth token: " + req.session.authToken);
}
// Rendering in Express loads an html page. EJS stands for embedded javascript. It can include dynamic variables, but when it
// renders it compiles to html that the browser can load.
res.render("index.ejs", {
err: req.session.err
});
// Only display the error once
req.session.err = null;
}
);
} else {
res.render("index.ejs", {
err: req.session.err
});
// Only display the error once
req.session.err = null;
}
});
// When a user submits the form to add a user, it makes a post request to /users and passes the name of the user
// via req.body.Username. the code below handles that post request by calling the REST API's function to add
// a user.
app.post('/users', function(req,res) {
console.log("Request to add user: " + req.body.Username);
// We will add the user to the specified site but first we have to get the id of that site.
// This is a common theme with the REST API. If you want to do something in a specific site, you must
// query that site to find out it's id. Similarly, if you want to do something with a specific user,
// you have to query to find that user's id.
request(
{
url: 'http://' + SERVERURL + '/api/2.0/sites/' + SITE + '?key=name',
headers: {
'Content-Type': 'text/xml',
'X-Tableau-Auth': req.session.authToken
}
},
function(err, response, body) {
if(err) {
req.session.err = err;
res.redirect('/');
} else {
var bodyXML = new jsxml.XML(body);
req.session.SiteID = bodyXML.child('site').attribute("id").getValue();
console.log("site id: " + req.session.SiteID);
}
// OK. We have the site, and we've stored it in the session cookie, now we add our new user to that site.
//First, build the XML for the POST
var reqxml = new XMLWriter();
reqxml.startElement('tsRequest').startElement('user')
.writeAttribute('name', req.body.Username).writeAttribute('role', 'Interactor')
.writeAttribute('publish', 'true').writeAttribute('contentAdmin','false')
.writeAttribute('suppressGettingStarted', 'true');
request.post(
{
url: 'http://' + SERVERURL + '/api/2.0/sites/' + req.session.SiteID + '/users/',
body: reqxml.toString(),
headers: {
'Content-Type': 'text/xml',
'X-Tableau-Auth': req.session.authToken
}
},
function(err, response, body) {
if(err) {
req.session.err = err;
} else {
//If the request was succesful we get xml back that contains the id and name of the added user.
var bodyXML = new jsxml.XML(body);
var userID = bodyXML.child('user').attribute('id').getValue();
var userName = bodyXML.child('user').attribute('name').getValue();
console.log(userName + " added with user id " + userID);
}
res.redirect('/users');
}
);
}
);
});
// Navigating to /users with the browser (as opposed to making a POST to users) will render a page
// with the list of users on the server, on the specified site.
app.get('/users', function(req,res) {
console.log("List of users requested.");
// We will grab the list of users from the specified site, but first we have to grab the site id
// (Same idea as when we added users. We could have checked if req.session.SiteID has been populated,
// but I chose to keep it simple instead)
request(
{
url: 'http://' + SERVERURL + '/api/2.0/sites/' + SITE + '?key=name',
headers: {
'Content-Type': 'text/xml',
'X-Tableau-Auth': req.session.authToken
}
},
function(err, response, body) {
if(err) {
req.session.err = err;
res.redirect('/');
} else {
var bodyXML = new jsxml.XML(body);
req.session.SiteID = bodyXML.child('site').attribute("id").getValue();
console.log("site id: " + req.session.SiteID);
}
// OK. We have the site, now let's grab the list of users
// Since we're just making a GET request, we don't need to build the xml. All the is needed
// is the SiteID which is inserted in the url and the auth token which is included in the headers
request(
{
url: 'http://' + SERVERURL + '/api/2.0/sites/' + req.session.SiteID + '/users/',
headers: {
'Content-Type': 'text/xml',
'X-Tableau-Auth': req.session.authToken
}
},
function(err, response, body) {
if(err) {
req.session.err = err;
} else {
// A succesful request returns xml with a <users> which contains multiple <user> elements.
// The <user> elements have name attributes and id attributes which we'll grab, store in a
// javascript object and render those in the html that loads.
var bodyXML = new jsxml.XML(body);
bodyXML.descendants('user').each(function(item, index) {
userIDs[item.attribute('name').getValue()] = item.attribute('id').getValue();
});
for(var user in userIDs) {
console.log(user + " " + userIDs[user]);
}
}
res.render("users.ejs", {
err: req.session.err,
userIDs: userIDs
});
// Only display the error once
req.session.err = null;
}
);
}
);
});
// On the list of users page, a user can click on a user's name. That will link them to /users/<username>.
// This is the route that handles that. It queries for all of the workbooks published by that user and then
// prints them out.
app.get('/users/:user', function(req, res) {
console.log('Requested: workbooks published by ' + req.params.user);
var workbooks = [];
// This is a similar GET request to getting the list of users. The difference is that instead of querying
// .../users/userid we query .../users/userid/workbooks
request(
{
url: 'http://' + SERVERURL + '/api/2.0/sites/' + req.session.SiteID
+ '/users/' + userIDs[req.params.user] + '/workbooks',
headers: {
'Content-Type': 'text/xml',
'X-Tableau-Auth': req.session.authToken
}
},
function(err, response, body) {
if(err) {
req.session.err = err;
} else {
// The returned xml is similar to the users xml. It contains a <workbooks> element that contains multiple
// <workbook> elements. We will grab the name attribute from each workbook, store it in an array and
// render it in a list in the html.
var bodyXML = new jsxml.XML(body);
bodyXML.descendants('workbook').each(function(item, index) {
workbooks.push(item.attribute('name').getValue());
});
for(var i = 0; i < workbooks.length; i++) {
console.log(workbooks[i]);
}
}
res.render("user.ejs", {
err: req.session.err,
user: req.params.user,
userID: userIDs[req.params.user],
workbooks: workbooks
});
// Only display the error once
req.session.err = null;
}
);
});
//Start this thing
var port = Number(process.env.PORT || 8001);
app.listen(port);
console.log("Listening on port " + port);