pryv 2.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsdoc-conf.json +29 -0
- package/.mocharc.js +13 -0
- package/LICENSE.md +27 -0
- package/README.md +723 -0
- package/package.json +57 -0
- package/scripts/setup-environment-dev.sh +28 -0
- package/scripts/upload.sh +15 -0
- package/src/Auth/AuthController.js +276 -0
- package/src/Auth/AuthStates.js +20 -0
- package/src/Auth/LoginMessages.js +29 -0
- package/src/Auth/index.js +43 -0
- package/src/Browser/CookieUtils.js +51 -0
- package/src/Browser/LoginButton.js +199 -0
- package/src/Browser/index.js +55 -0
- package/src/Connection.js +331 -0
- package/src/Pryv.js +19 -0
- package/src/Service.js +197 -0
- package/src/ServiceAssets.js +162 -0
- package/src/index-socket.io-monitor.js +4 -0
- package/src/index.html +17 -0
- package/src/index.js +3 -0
- package/src/lib/browser-getEventStreamed.js +80 -0
- package/src/lib/json-parser.js +156 -0
- package/src/utils.js +136 -0
- package/test/Browser.AuthController.test.js +97 -0
- package/test/Browser.test.js +79 -0
- package/test/Connection.test.js +455 -0
- package/test/Service.test.js +89 -0
- package/test/ServiceAssets.test.js +79 -0
- package/test/Y.png +0 -0
- package/test/browser-index.js +11 -0
- package/test/browser-tests.html +31 -0
- package/test/helpers.js +8 -0
- package/test/load-test-account.js +108 -0
- package/test/test-data.js +92 -0
- package/test/utils.test.js +68 -0
- package/web-demos/auth-with-redirection.html +72 -0
- package/web-demos/auth.html +77 -0
- package/web-demos/custom-login-button.html +158 -0
- package/web-demos/index.html +186 -0
- package/web-demos/service-info.json +13 -0
- package/web-demos/stream-examples.html +80 -0
- package/webpack.config.js +71 -0
package/src/utils.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
|
|
2
|
+
const regexAPIandToken = /(.+):\/\/(.+)@(.+)/gm;
|
|
3
|
+
const regexSchemaAndPath = /(.+):\/\/(.+)/gm;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utilities to access Pryv API.
|
|
7
|
+
* Exposes superagent and methods to manipulate Pryv's api endpoints
|
|
8
|
+
* @memberof Pryv
|
|
9
|
+
* @namespace Pryv.utils
|
|
10
|
+
*/
|
|
11
|
+
const utils = {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Exposes superagent https://visionmedia.github.io/superagent/
|
|
15
|
+
* @memberof Pryv.utils
|
|
16
|
+
* @property {Superagent} superagent
|
|
17
|
+
*/
|
|
18
|
+
superagent: require('superagent'),
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns true is run in a browser
|
|
22
|
+
* @memberof Pryv.utils
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
isBrowser: function() {
|
|
26
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* From a PryvApiEndpoint URL, return an object (TokenAndAPI) with two properties
|
|
32
|
+
* @memberof Pryv.utils
|
|
33
|
+
* @param {PryvApiEndpoint} pryvApiEndpoint
|
|
34
|
+
* @returns {TokenAndEndpoint}
|
|
35
|
+
*/
|
|
36
|
+
extractTokenAndApiEndpoint: function (pryvApiEndpoint) {
|
|
37
|
+
regexAPIandToken.lastIndex = 0;
|
|
38
|
+
const res = regexAPIandToken.exec(pryvApiEndpoint);
|
|
39
|
+
|
|
40
|
+
if (res !== null) { // has token
|
|
41
|
+
// add a trailing '/' to end point if missing
|
|
42
|
+
if (!res[3].endsWith('/')) {
|
|
43
|
+
res[3] += '/';
|
|
44
|
+
}
|
|
45
|
+
return { endpoint: res[1] + '://' + res[3], token: res[2] }
|
|
46
|
+
}
|
|
47
|
+
// else check if valid url
|
|
48
|
+
regexSchemaAndPath.lastIndex = 0;
|
|
49
|
+
const res2 = regexSchemaAndPath.exec(pryvApiEndpoint);
|
|
50
|
+
if (res2 === null) {
|
|
51
|
+
throw new Error('Cannot find endpoint, invalid URL format');
|
|
52
|
+
}
|
|
53
|
+
// add a trailing '/' to end point if missing
|
|
54
|
+
if (!res2[2].endsWith('/')) {
|
|
55
|
+
res2[2] += '/';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { endpoint: res2[1] + '://' + res2[2] , token: null }
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get a PryvApiEndpoint URL from a TokenAndAPI object
|
|
63
|
+
* @memberof Pryv.utils
|
|
64
|
+
* @param {TokenAndEndpoint} tokenAndApi
|
|
65
|
+
* @returns {PryvApiEndpoint}
|
|
66
|
+
*/
|
|
67
|
+
buildPryvApiEndpoint: function (tokenAndApi) {
|
|
68
|
+
if (! tokenAndApi.token) {
|
|
69
|
+
let res = tokenAndApi.endpoint + '';
|
|
70
|
+
if (!tokenAndApi.endpoint.endsWith('/')) {
|
|
71
|
+
res += '/';
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
regexSchemaAndPath.lastIndex = 0;
|
|
76
|
+
let res = regexSchemaAndPath.exec(tokenAndApi.endpoint);
|
|
77
|
+
// add a trailing '/' to end point if missing
|
|
78
|
+
if (!res[2].endsWith('/')) {
|
|
79
|
+
res[2] += '/';
|
|
80
|
+
}
|
|
81
|
+
return res[1] + '://' + tokenAndApi.token + '@' + res[2];
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} [navigatorForTests] mock navigator var only for testing purposes
|
|
87
|
+
*/
|
|
88
|
+
browserIsMobileOrTablet: function (navigator) {
|
|
89
|
+
if (navigator == null) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
let check = false;
|
|
93
|
+
(function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || navigator.opera);
|
|
94
|
+
return check;
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
cleanURLFromPrYvParams: function (url) {
|
|
98
|
+
const PRYV_REGEXP = /[?#&]+prYv([^=&]+)=([^&]*)/g;
|
|
99
|
+
return url.replace(PRYV_REGEXP, '');
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
getQueryParamsFromURL: function (url) {
|
|
103
|
+
let vars = {};
|
|
104
|
+
const QUERY_REGEXP = /[?#&]+([^=&]+)=([^&]*)/g;
|
|
105
|
+
url.replace(QUERY_REGEXP,
|
|
106
|
+
function (m, key, value) {
|
|
107
|
+
vars[key] = decodeURIComponent(value);
|
|
108
|
+
});
|
|
109
|
+
return vars;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = utils;
|
|
114
|
+
|
|
115
|
+
// --------------- typedfs ------------------------------- //
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* An object with two properties: token & apiEndpoint
|
|
119
|
+
* @typedef {Object} TokenAndEndpoint
|
|
120
|
+
* @property {string} [token] Authorization token
|
|
121
|
+
* @property {string} endpoint url of Pryv api endpoint
|
|
122
|
+
*/
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A String url of the form http(s)://{token}@{apiEndpoint}
|
|
126
|
+
* @typedef {string} PryvApiEndpoint
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Common Meta are returned by each standard call on the API https://api.pryv.com/reference/#in-method-results
|
|
132
|
+
* @typedef {Object} CommonMeta
|
|
133
|
+
* @property {string} apiVersion The version of the API in the form {major}.{minor}.{revision}. Mirrored in HTTP header API-Version.
|
|
134
|
+
* @property {number} serverTime The current server time as a timestamp in second. Keeping track of server time is necessary to properly handle time in API calls.
|
|
135
|
+
* @property {string} serial The serial will change every time the core or register is updated. If you compare it with the serial of a previous response and notice a difference, you should reload the service information.
|
|
136
|
+
*/
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const expect = chai.expect;
|
|
2
|
+
|
|
3
|
+
const utils = require('../src/utils.js');
|
|
4
|
+
const Service = require('../src/Service');
|
|
5
|
+
const AuthController = require('../src/Auth/AuthController.js');
|
|
6
|
+
const testData = require('./test-data.js');
|
|
7
|
+
|
|
8
|
+
describe('Browser.LoginButton', function() {
|
|
9
|
+
this.timeout(5000);
|
|
10
|
+
|
|
11
|
+
let auth;
|
|
12
|
+
let removeZombie = false;
|
|
13
|
+
before(async function() {
|
|
14
|
+
if (typeof document !== 'undefined') return; // in browser
|
|
15
|
+
removeZombie = true;
|
|
16
|
+
const browser = new Browser();
|
|
17
|
+
browser.visit('./?pryvServiceInfoUrl=https://zouzou.com/service/info');
|
|
18
|
+
global.document = browser.document;
|
|
19
|
+
global.window = browser.window;
|
|
20
|
+
global.location = browser.location;
|
|
21
|
+
global.navigator = { userAgent: 'Safari' };
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
after(async function() {
|
|
25
|
+
if (!removeZombie) return; // in browser
|
|
26
|
+
delete global.document;
|
|
27
|
+
delete global.window;
|
|
28
|
+
delete global.location;
|
|
29
|
+
});
|
|
30
|
+
before(async function() {
|
|
31
|
+
let service = new Service(testData.serviceInfoUrl);
|
|
32
|
+
await service.info();
|
|
33
|
+
auth = new AuthController({
|
|
34
|
+
authRequest: {
|
|
35
|
+
requestingAppId: 'lib-js-test',
|
|
36
|
+
requestedPermissions: []
|
|
37
|
+
}
|
|
38
|
+
}, service);
|
|
39
|
+
await auth.init();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('getReturnURL()', async function() {
|
|
43
|
+
const myUrl = 'https://mysite.com/bobby';
|
|
44
|
+
let error = null;
|
|
45
|
+
try {
|
|
46
|
+
auth.getReturnURL('auto');
|
|
47
|
+
} catch (e) {
|
|
48
|
+
error = e;
|
|
49
|
+
}
|
|
50
|
+
expect(error).to.be.not.null;
|
|
51
|
+
|
|
52
|
+
let fakeNavigator = { userAgent: 'android' };
|
|
53
|
+
expect(auth.getReturnURL('auto#', myUrl, fakeNavigator)).to.equal(myUrl + '#');
|
|
54
|
+
expect(auth.getReturnURL('auto?', myUrl, fakeNavigator)).to.equal(myUrl + '?');
|
|
55
|
+
expect(auth.getReturnURL(false, myUrl, fakeNavigator)).to.equal(myUrl + '#');
|
|
56
|
+
expect(auth.getReturnURL('self?', myUrl, fakeNavigator)).to.equal(myUrl + '?');
|
|
57
|
+
|
|
58
|
+
expect(auth.getReturnURL('http://zou.zou/toto#', myUrl, fakeNavigator)).to.equal('http://zou.zou/toto#');
|
|
59
|
+
|
|
60
|
+
fakeNavigator = { userAgent: 'Safari' };
|
|
61
|
+
expect(auth.getReturnURL('auto#', myUrl, fakeNavigator)).to.equal(false);
|
|
62
|
+
expect(auth.getReturnURL('auto?', myUrl, fakeNavigator)).to.equal(false);
|
|
63
|
+
expect(auth.getReturnURL(false, myUrl, fakeNavigator)).to.equal(false);
|
|
64
|
+
expect(auth.getReturnURL('self?', myUrl, fakeNavigator)).to.equal(myUrl + '?');
|
|
65
|
+
expect(auth.getReturnURL('http://zou.zou/toto#', myUrl, fakeNavigator)).to.equal('http://zou.zou/toto#');
|
|
66
|
+
global.window = { location: { href: myUrl + '?prYvstatus=zouzou'} }
|
|
67
|
+
expect(auth.getReturnURL('self?', myUrl, fakeNavigator)).to.equal(myUrl + '?');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('browserIsMobileOrTablet()', async function() {
|
|
71
|
+
expect(utils.browserIsMobileOrTablet({ userAgent: 'android' })).to.be.true;
|
|
72
|
+
expect(utils.browserIsMobileOrTablet({ userAgent: 'Safari' })).to.be.false;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
it('cleanURLFromPrYvParams()', async function() {
|
|
77
|
+
|
|
78
|
+
expect('https://my.Url.com/?bobby=2').to.equal(utils.cleanURLFromPrYvParams(
|
|
79
|
+
'https://my.Url.com/?bobby=2&prYvZoutOu=1&prYvstatus=2jsadh'));
|
|
80
|
+
|
|
81
|
+
expect('https://my.Url.com/?pryvServiceInfoUrl=zzz').to.equal(utils.cleanURLFromPrYvParams(
|
|
82
|
+
'https://my.Url.com/?pryvServiceInfoUrl=zzz#prYvZoutOu=1&prYvstatus=2jsadh'));
|
|
83
|
+
|
|
84
|
+
expect('https://my.Url.com/').to.equal(utils.cleanURLFromPrYvParams(
|
|
85
|
+
'https://my.Url.com/?prYvstatus=2jsadh'));
|
|
86
|
+
|
|
87
|
+
expect('https://my.Url.com/').to.equal(utils.cleanURLFromPrYvParams(
|
|
88
|
+
'https://my.Url.com/#prYvstatus=2jsadh'));
|
|
89
|
+
|
|
90
|
+
expect('https://my.Url.com/#bobby=2').to.equal(utils.cleanURLFromPrYvParams(
|
|
91
|
+
'https://my.Url.com/#bobby=2&prYvZoutOu=1&prYvstatus=2jsadh'));
|
|
92
|
+
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
const should = chai.should();
|
|
3
|
+
const expect = chai.expect;
|
|
4
|
+
|
|
5
|
+
const testData = require('./test-data.js');
|
|
6
|
+
|
|
7
|
+
function genSettings() {
|
|
8
|
+
function defaultStateChange(state) {
|
|
9
|
+
console.log('Test unimplemented on state change', state);
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
authRequest: {
|
|
13
|
+
requestingAppId: 'lib-js-test',
|
|
14
|
+
requestedPermissions: [{ streamId: '*', level: 'read' }],
|
|
15
|
+
},
|
|
16
|
+
onStateChange: defaultStateChange
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('Browser', function () {
|
|
21
|
+
this.timeout(5000);
|
|
22
|
+
|
|
23
|
+
before(async function () {
|
|
24
|
+
this.timeout(5000);
|
|
25
|
+
await testData.prepare();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
let removeZombie = false;
|
|
29
|
+
|
|
30
|
+
before(async () => {
|
|
31
|
+
if (typeof document !== 'undefined') return; // in browser
|
|
32
|
+
removeZombie = true;
|
|
33
|
+
const browser = new Browser();
|
|
34
|
+
browser.visit('./?pryvServiceInfoUrl=https://zouzou.com/service/info');
|
|
35
|
+
global.document = browser.document;
|
|
36
|
+
global.window = browser.window;
|
|
37
|
+
global.location = browser.location;
|
|
38
|
+
global.navigator = {userAgent: 'Safari'};
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
after(async () => {
|
|
42
|
+
if (!removeZombie) return; // in browser
|
|
43
|
+
delete global.document;
|
|
44
|
+
delete global.window;
|
|
45
|
+
delete global.location;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('setupAuth()', async () => {
|
|
49
|
+
const settings = genSettings();
|
|
50
|
+
let AuthLoaded = false;
|
|
51
|
+
settings.onStateChange = function (state) {
|
|
52
|
+
should.exist(state.id);
|
|
53
|
+
if (state.id == Pryv.Auth.AuthStates.LOADING) {
|
|
54
|
+
AuthLoaded = true;
|
|
55
|
+
}
|
|
56
|
+
if (state.id == Pryv.Auth.AuthStates.INITIALIZED) {
|
|
57
|
+
expect(AuthLoaded).to.true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const service = await Pryv.Auth.setupAuth(settings, testData.serviceInfoUrl);
|
|
63
|
+
const serviceInfo = service.infoSync();
|
|
64
|
+
should.exist(serviceInfo.access);
|
|
65
|
+
should.exist(serviceInfo.serial);
|
|
66
|
+
} catch(error) {
|
|
67
|
+
console.log(error);
|
|
68
|
+
should.not.exist(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
it('serviceInfoFromUrl()', async () => {
|
|
74
|
+
expect('https://zouzou.com/service/info').to.equal(Pryv.Browser.serviceInfoFromUrl());
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
|