forked from remotestorage/armadietto
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathload.html
345 lines (304 loc) · 13.4 KB
/
load.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>RemoteStorage Load Test</title>
<script src="https://cdn.jsdelivr.net/npm/remotestoragejs@latest/release/remotestorage.js"></script>
<script src="https://cdn.jsdelivr.net/npm/remotestorage-widget@latest/build/widget.js"></script>
<!-- ignore below :: code pane / logging pane setup -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!-- only needed for logging and code panes -->
<script src="ignore/logging.js"></script> <!-- helper JS functionality to support showing source code for this file in the UI -->
<link rel="stylesheet" type="text/css" href="ignore/styles.css">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<!-- ignore above :: code pane / logging pane setup -->
</head>
<body>
<p id="getwider"> |<---->| Cannot view, screen to narrow, please revisit on a wider device.</p>
<div id="window">
<div id="demoview">
<div id="remotestorage-widget-anchor"></div>
<!--- =========================================================================
** UI Forms **
========================================================================= -->
<!-- serverInfo -->
<div class="w3-panel w3-card w3-light-grey">
<form id="serverInfo"><p>
<input name="server" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">server</label>
<input name="user" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">user</label>
<input name="token" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">token</label>
<input name="scope" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">scope</label>
</p></form>
</div>
<!-- load -->
<div class="w3-panel w3-card">
<details><summary class="usecase">LOAD TEST over remoteStorage.js</summary><p>
<em>remoteStorage.js</em> abstracted API calls to the server.
<br/><br/>
Use <em>widget</em> (above) to retrieve the <em>token</em>.
<br/><br/>
Check log-pane for results.
</p></details>
<form id="apiStorage"><p>
<input name="token" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">token</label>
<input name="scope" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">scope</label>
<p><select name="httpMethod" class="w3-select" onchange="scrubForm(event)">
<option value="PUT" selected>PUT</option>
<option value="GET">GET</option>
<option value="DELETE">DELETE</option>
</select><label class="w3-text-blue">HTTP method for call</label></p>
<p><input name="iterations" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">iterations (number files to write)</label></p>
<p><input name="size" class="w3-input" type="text" onchange="scrubForm(event)"><label class="w3-text-blue">size (characters per file)</label></p>
</p></form>
<p><button class="w3-btn w3-blue w3-left-align w3-block" onclick="load()">run load()</button></p>
</div>
</div>
<div id="panes">
<div id="logview">
<div id="logviewcontents"></div>
</div>
<div id="codeview">
<iframe id="codeframe" src="ignore/code.html#welcome.txt"></iframe>
</div>
</div>
</div>
</body>
<script>
/* =========================================================================
** Initialization **
========================================================================= */
// - save off URL params from load/refresh as widget will mangle them
// - init logging whenever page refreshed
// - create a new global 'remoteStorage' instance whenever screen refreshed
// - re-init 'data' including any 'state' values from URL in case of manual (non-widget) oauth redirect
// - see gotoAuth and postOAuth
// - pre-fill all forms based on loaded/default 'data'
// - on DOM load initialize, attach widget, starts widget event listening to global 'remoteStorage'
// - if was connected in localstorage before refresh/load, global 'remoteStorage' reconnects
// - if was disconnected in localstorage (e.g. calls to gotoAuth and postOAuth disconnect) before refresh/load, then global 'remoteStorage' stays disconnected
const params = (new URLSearchParams(window.location.hash.substr(1))); // extract hash as params, as per draft-dejong
log('loading ', {href: window.location.href, params: params.toString()});
window.onload = function () {
showPostScreenSetupLogs(); // ignore above :: logging setup
}
log('initialize remoteStorage used by app: widget and non-widget');
var remoteStorage = new RemoteStorage({cache: false});
var remoteStorageConnected = false;
log("initialize data -- state for use-cases -- to empty");
var data = {
scope: 'armadietto-example-load:rw',
responseType: 'token',
token: params.get('access_token'),
};
unmarshal();
fillForms();
document.addEventListener('DOMContentLoaded', function() {
remoteStorage.access.claim(data.scope, 'rw');
const widget = new Widget(remoteStorage);
attachEvents('appStorage', remoteStorage);
log('in DOMContentLoaded :: attaching remoteStorage to widget');
log('attaching to #remotestorage-widget-anchor element');
widget.attach('remotestorage-widget-anchor');
});
/* =========================================================================
** Supporting Methods / Utilities **
========================================================================= */
/**
* Marshall select 'data' state for URL passing
*/
function marshal() {
var {server, user, remotestorageHref} = data;
return btoa(JSON.stringify({ server: server, user: user, remotestorageHref: remotestorageHref }));
}
/**
* Unmarshal select 'data' state from passed in URL
*/
function unmarshal() {
if (params.has('state') && params.get('state')) {
var state = JSON.parse(atob(params.get('state')));
log(`unmarshalled 'state' from URL params`, state);
updateData(state);
}
}
/**
* Update use-case 'data' (global) with JSON passed in.
*
* SIDE EFFECT: refreshes all forms.
*/
function updateData(withWhat) {
data = {...data, ...withWhat};
fillForms();
}
/**
* Scrub form fields of the caller's form into 'data'
*
* @param {} event -- event originating from an input within a form to scrub
*/
function scrubForm(event) {
var form = event.target.form;
var json = {};
for (element of form) {
json[element.name] = element.value;
}
updateData(json);
}
/**
* Fill form fields from JSON
*
* @param {string} formId - element ID of the form to fill
* @param {Object} values - object to source values from by keys matching form field 'names'
* @param {boolean} disabled - if true, the form is disabled, default, false
*/
function disableForm(formId, disabled) {
var form = document.getElementById(formId);
for (element of form) {
element.disabled = !!disabled;
}
}
/**
* Go through all forms and fill matching fields with 'data' (data attribut -> field name)
*/
function fillForms() {
for (form of document.forms) {
for (element of form) {
if (element.name in data) {
element.value = data[element.name];
}
}
}
}
/* =========================================================================
** Events **
========================================================================= */
/**
* Handle events from remotestorage instance.
*
* @param {String} tag -- descriptive tag for remotestorage instance
* @param {} rsInstance -- to handle events for
*/
function attachEvents(tag, rsInstance) {
log('initialize remotestorage event handlers for instance :: ' + tag);
rsInstance.on('connected', onConnected);
rsInstance.on('not-connected', onNotConnected);
rsInstance.on('ready', onReady);
rsInstance.on('disconnected', onDisconnect);
rsInstance.on('error', onError);
function onConnected (event) {
log(`onConnected :: start`, {tag: tag, event: event})
let userLineParts = rsInstance.remote.userAddress.split('@');
let protocol = rsInstance.remote.href.match(/^[a-z]+:\/\//ig);
let scope = Object.keys(rsInstance.access.scopeModeMap)[0];
let token = rsInstance.remote.token;
updateData({
user: userLineParts[0],
server: protocol + userLineParts[1],
scope: scope,
token: token});
log(`onConnected :: set data`, {tag: tag, event: event, data: data});
log(`onConnected :: disabling serverInfo entry`,{tag: tag, event: event});
disableForm('serverInfo', true);
remoteStorageConnected = true;
}
function onNotConnected (event) {
log(`onNotConnected :: start`, {tag: tag, event: event})
log(`onConnected :: re-enabling serverInfo entry`, {tag: tag, event: event});
disableForm('serverInfo', false);
remoteStorageConnected = false;
}
function onReady (event) {
log(`onReady :: start`, {tag: tag, event: event})
}
function onDisconnect (event) {
log(`onDisconnect :: start`, {tag: tag, event: event})
}
function onError (event) {
log(`onError :: start`, {tag: tag, event: event})
}
}
/* =========================================================================
** Form Actions / Use Case Functions **
========================================================================= */
async function load() {
log('load :: start');
if (!data.token || !data.scope || !data.server || !data.iterations || !data.size) {
log('load :: required data properties not present, click this log to see expression', data);
return;
}
if (!remoteStorageConnected) {
let server = data.server.split('://').reduce((c,n) => n,'');
let address = `${data.user}@${server}`;
log('load :: connecting new remoteStorage', { address, address });
remoteStorage.connect(address, data.token);
log('load :: PLEASE RERUN :: once you see onConnected in log :: we had to re-connect');
return;
}
let category = data.scope.split(':')[0];
let client = remoteStorage.scope(`/${category}/`);
data.data = randomChars(data.size)
let successes = 0;
let failures = 0;
let timeStart = parseInt(Date.now());
let promises = [];
let fn = async (path) => {
let url = `${data.server}/storage/${data.user}/${category}/${path}`;
try {
let payload = {
method: data.httpMethod,
headers: {
"Authorization": `Bearer ${data.token}`
}
};
switch (data.httpMethod) {
case 'PUT':
payload = {
...payload,
headers: {
...payload.headers,
"Content-Type": "text/plain"
},
body: data.data
}
break;
case 'GET':
break;
case 'DELETE':
break;
}
let {etag, code, result} = await fetch(url, payload)
.then(res => {return {etag: res.headers.get('ETag'), code: res.status, result: res.text()}})
.catch(error => log(`httpStorage :: error`,{error: error}));
result = await result;
if (code !== 200 && code !== 404) {
failures++;
log(`load test :: retry successes:${successes} failures:${failures} ${path}`);
if (!remoteStorageConnected) return;
return fn(path);
}
successes++;
return result;
}
catch (e) {
failures++;
log(`load test :: retry successes:${successes} failures:${failures} ${path}`);
if (!remoteStorageConnected) return;
return fn(path);
}
};
for (var i = 0; i < data.iterations; i++) {
data.path = `file${i}`;
let path = data.path ? data.path.split('/').filter(t => t.length > 0).join('/') : '';
promises.push(fn(path));
}
await Promise.all(promises);
let timeEnd = parseInt(Date.now());
log(`load results :: ${data.apiMethod} x${data.iterations} @${data.size} chars - ${(timeEnd - timeStart) / 1000} seconds`);
}
function randomChars(length) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < length; i++)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
</script>
</html>