📜 ⬆️ ⬇️

Create a project with OAuth and NoSQL for $ 0.00

For a very long time, I wanted to try to create a project that would be a real JavaScript Application, namely a fat client, without a backend and my hosting, based on open source and some BaaS / DaaS . Besides, I was finally tired of jsperf.com , of these meaningless two steps, the absence of at least some code editor and normal search and the constant loss of my tests, and the story with the captcha, which does not always work, finally finished me off. I finally took the time to implement the long conceived and kill two birds with one stone, implementing the alternative jsperf.



So, first of all the requirements for the project:


And now the most interesting: where to store the source of the tests? And the results?
')

Where to store the source?


If there are regular users of jsperf among you, they remember the recent history when it was completely unavailable precisely because of the storage of the code and the results of the test run. So the task came down to one thing: how to do it so that nothing is stored in oneself, but to transfer it to some service, and better to the user? The answer suggested itself: the ideal place to store the source code is GitHub , more precisely, Gist . Almost every developer has it, and this solves several tasks at once:


GitHub has a wonderful REST API , this is probably one of the reference implementations, there is also an API for working with Gist. The question remained for small: how to keep Gist on behalf of the user?

Oauth


For authorization, GitHub offers to use OAuth , this is easy, but requires a minimum backend. There were several ways to go:

  1. Find some free hosting or BaaS and deploy one of the open source GitHub solutions there.
  2. Use the service OAuth.io , which has an acceptable free-plan.

I chose OAuth.io as a very simple and fast way to get started, moreover, if necessary, you can get rid of the service without serious consequences . Plus, he has a good analytics, a simple lib for the work of the API and a bunch of providers for any service, including GitHub. And the buzz that to get started you don’t even have to go through a tedious registration, just click “Sign in with GitHub” and add the keys to your application.

Github API


The next step is to write a wrapper for working with the GitHub API. And there is a small nuance: I really wanted to once again not pull OAuth.io, so as not to go beyond the limits of the free-plan. As it turned out, GitHub allows accessing unauthorized APIs, but such calls are tightly limited, so the Gist getting method has quite a non-trivial logic:

  1. We check Runtime cache, if there is data, we give.
  2. If there is data about the user in localStorage , we consider that it is already authorized, call the token getting through OAuth.io and make a request to the API. If the authorization fails, send the request to an unauthorized one and hope that the limits are not yet exhausted.
  3. If localStorage nothing in localStorage , we make the request as unauthorized, in case of an error, we try to log in via OAuth.io and repeat the request already as authorized.

We translate it into code, sprinkle Promise + fetch and get the following method:

 function findOne(id) { let promise; const url = 'gists/' + id; const _fetch = () => { return fetch(API_ENDPOINT + url).then(res => { if (res.status !== API_STATUS_OK) { throw 'Error: ' + res.status; } return res.json(); }); }; if (_gists[id]) { // Runtime cache promise = Promise.resolve(_gists[id]); } else if (github.currentUser) { //  ,  Gist  OAuth.io promise = _call('get', url)['catch'](() => { // ,     GitHub API github.setUser(null); return _fetch(); }); } else { //  ,    GitHub API promise = _fetch()['catch'](() => { // ,      return _call('get', url); }); } return promise.then(gist => { //   Runtime cache _gists[gist.id] = gist; return gist; }); } 


Where to store the results?


If I decided on the source quickly, then the results are not so simple. I will not torment, just list the solutions that I knew at the time:


Since the project was experimental, the choice fell on Firebase: besides the JavaScript API, it offered twice as much space as on the same MongoLab, a whole gigabyte.

In fact, there was another option: localStorage / IndexedDB + WebRTC . The idea was as follows: we store the results of the runs in localStorage and if there is someone else online, then we synchronize the data :).

So, Firebase. To use it to disgrace is simple, the documentation does not lie: https://www.firebase.com/docs/web/quickstart.html .

 //   Firebase (  application,     JSBench) const firebase = new Firebase('https://jsbench.firebaseio.com/'); //     «» (stats / {gist_id} / {revision_id}): firebase.child('stats').child(gist.id).child(getGistLastRevisionId(gist)).on('value', (snapshot) => { const values = snapshot.val(); //   }); // -    -    firebase.child('stats').child(gist.id).child(getGistLastRevisionId(gist)).push(data); 

This is all the code that I had to write to work with Firebase, but the great thing is that the “host” update event is triggered whenever someone runs benchmarks, and you get online without any F5 or cmd + r, you get update charts.

Design


Oh, that's what I do not know, I do not know how, so everything looks like this:


The interface is as informative as possible, the main important parameters are displayed, the result of the run is displayed to the right of the test code, after the test is completed, the lines are highlighted in color, and graphs are constructed under the code. Setup and Teardown made in the "ears" at the bottom of the screen - the decision is controversial, but suitable for most tasks. As a result, all possible information fits on one screen.

As you can see, unlike jsperf, I have code highlighting, using Ace for this.

Ace is a great tool for integrating a code editor into your application. To use it:

 //   const editor = ace.edit(this.el); //   editor.setTheme('ace/theme/tomorrow'); //   JavaScript editor.getSession().setMode('ace/mode/javascript'); //       editor.setOption('maxLines', 30); editor.setOption('minLines', 4); //   editor.$blockScrolling = Number.POSITIVE_INFINITY; //    editor.on('change', () => { const value = editor.getValue(); // ... }); 

For the test run, Platform.js and Benchmark.js are used , I draw graphs using Google Visualization , so the run results and graphs look exactly the same as on jsperf.

Sharing


One of the features is the sharing of the test, now only Twitter and Facebook are supported.

Twitter


There is nothing special to tell: open a popup with pre-installed text, and then the user himself decides whether to post or not.
 function twitter(desc, url, tags) { const max = 130; const top = Math.max(Math.round((SCREEN_HEIGHT / 3) - (twttr.height / 2)), 0); const left = Math.round((SCREEN_WIDTH / 2) - (twttr.width / 2)); const message = desc.substr(0, max - (url.length + tags.length)) + ': ' + url + ' ' + tags; const params = 'left=' + left + ',top=' + top + ',width=' + twttr.width + ',height=' + twttr.height; const extras = ',personalbar=0,toolbar=0,scrollbars=1,resizable=1'; window.open(twttr.url + encodeURIComponent(message), 'twitter', params + extras); } 

Facebook


Here it is more interesting, I wanted not just to post a link, but immediately a chart to the tape. Google Visualization has a method for getting dataURI , while FB has a Graph API , it remains to be their friends:
Binding over Facebook SDK
 const facebook = { appId: 'XXXXXXX', publichUrl: 'https://graph.facebook.com/me/photos', init() { return this._promiseInit || (this._promiseInit = new Promise(resolve => { window.fbAsyncInit = () => { const FB = window.FB; FB.init({ appId: this.appId, version: 'v2.5', cookie: true, oauth: true }); resolve(FB); }; //    (function (d, s, id) { var fjs = d.getElementsByTagName(s)[0], js; if (d.getElementById(id)) {return;} js = d.createElement(s); js.id = id; js.src = '//connect.facebook.net/en_US/sdk.js'; fjs.parentNode.insertBefore(js, fjs); })(document, 'script', 'facebook-jssdk'); })); }, login() { return this._promiseLogin || (this._promiseLogin = this.init().then(api => { return new Promise((resolve, reject) => { api.login((response) => { if (response.authResponse) { resolve(response.authResponse.accessToken); } else { reject(new Error('Access denied')); } }, { scope: 'publish_actions' }); }); })); } }; 

To convert dataURI we use https://github.com/blueimp/JavaScript-Canvas-to-Blob/.

And publish:

 function facebookPublish(dataURI, message) { return facebook.login().then(token => { const file = dataURLtoBlob(dataURI); const formData = new FormData(); formData.append('access_token', token); formData.append('source', file); formData.append('message', message); return fetch(facebook.publishUrl, { method: 'post', mode: 'cors', body: formData }); }); } 

Future plans


Full list of used libraries and polyphiles



Total


As you can see, at the moment you can assemble a high-quality prototype using ready-made solutions and free platforms, spending only your time on it. But this is not just saving money - it is also saving time on the choice of hosting, installation, setting up the necessary software and further administration. All these problems disappear when you use any BaaS or DaaS - they give you a ready-made solution without a headache. Plus, if the project grows and gets stronger, you can always switch to a suitable paid rate or raise the exact same stack on your hosting.

Project page: http://jsbench.imtqy.com/
Source code and tasks: https://github.com/jsbench/jsbench.imtqy.com/

Source: https://habr.com/ru/post/272911/


All Articles