Comparing version 0.2.0 to 0.3.0
396
ovh.js
// | ||
// node-ovh | ||
// | ||
// @version 0.1.0 | ||
// @version 0.3.0 | ||
// @author Vincent Giersch <mail@vincent.sh> | ||
// @license MIT | ||
// | ||
@@ -10,3 +11,5 @@ | ||
querystring = require('querystring'), | ||
handlerMaker = require("./handler-maker"); | ||
url = require('url'), | ||
crypto = require('crypto'), | ||
handlerMaker = require('./handler-maker'); | ||
@@ -20,13 +23,100 @@ if (typeof(Proxy) === 'undefined') { | ||
function OVHWS(wsList, wsServer) { | ||
this.wsList = wsList; | ||
function OVHWS(wsList, apiKeys) { | ||
this.apiKeys = { appKey: apiKeys.appKey, appSecret: apiKeys.appSecret, | ||
consumerKey: apiKeys.consumerKey || null, | ||
credentialToken: apiKeys.credentialToken || null }; | ||
this.wsList = {}; | ||
this.wsMetas = {}; | ||
this.wsServer = wsServer; | ||
// Check and add implicit params in wsList | ||
for (var apiName in wsList) { | ||
if (wsList.hasOwnProperty(apiName)) { | ||
// Backward compatibility with <= v0.2, use as WS | ||
if (typeof(wsList[apiName]) === 'string') { | ||
this.wsList[apiName] = { type: 'WS', path: wsList[apiName], host: 'ws.ovh.com' }; | ||
} | ||
else if (typeof(wsList[apiName]) === 'object') { | ||
// Type and path | ||
if (typeof(wsList[apiName].type) === 'undefined') { | ||
throw new Error('OVH: `type` is a compulsory parameter'); | ||
} | ||
if (wsList[apiName].type !== 'WS' && wsList[apiName].type !== 'REST') { | ||
throw new Error('OVH: types supported are (WS|REST)'); | ||
} | ||
if (typeof(wsList[apiName].path) === 'undefined') { | ||
throw new Error('OVH: `path` is a compulsory parameter'); | ||
} | ||
this.wsList[apiName] = { type: wsList[apiName].type, path: wsList[apiName].path, host: wsList[apiName].host }; | ||
// Host, baseName for REST | ||
if (this.wsList[apiName].type === 'REST') { | ||
// Check for appKey or appSecret for REST usage | ||
if (typeof(this.apiKeys.appKey) !== 'string' || typeof(this.apiKeys.appSecret) !== 'string') { | ||
throw new Error('OVH API: You should precise an application key / secret'); | ||
} | ||
if (typeof(wsList[apiName].host) === 'string') { | ||
var host = url.parse(wsList[apiName].host); | ||
if (typeof(host.host) === 'string') { | ||
this.wsList[apiName].host = host.host; | ||
if (host.slashes) { | ||
this.wsList[apiName].basePath = host.pathname; | ||
} | ||
} | ||
else { | ||
this.wsList[apiName].host = wsList[apiName].host; | ||
} | ||
} | ||
if (typeof(wsList[apiName].basePath) === 'string') { | ||
this.wsList[apiName].basePath = wsList[apiName].basePath; | ||
} | ||
// Default values | ||
if (typeof(this.wsList[apiName].host) === 'undefined') { | ||
this.wsList[apiName].host = 'api.ovh.com'; | ||
} | ||
if (typeof(this.wsList[apiName].basePath) === 'undefined') { | ||
this.wsList[apiName].basePath = '/1.0'; | ||
} | ||
} | ||
// Host for WS | ||
else { | ||
if (typeof(this.wsList[apiName].host) !== 'string') { | ||
this.wsList[apiName].host = 'ws.ovh.com'; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return this.createRootProxy(); | ||
} | ||
// Default REST actions | ||
OVHWS.prototype.actionsREST = { | ||
'get': 'GET', | ||
'query': 'GET', | ||
'post': 'POST', | ||
'put': 'PUT', | ||
'remove': 'DELETE', | ||
'delete': 'DELETE' | ||
}; | ||
// Create handler for root proxy (OVHWS.{ws-name}.{method}{.call,}) | ||
OVHWS.prototype.createRootProxy = function () { | ||
// Proxy | ||
var handler = handlerMaker(this), | ||
_this = this; | ||
// Catch WS name get | ||
// Catch WS name | ||
handler.get = function (target, name) { | ||
if (typeof _this[name] !== "undefined") { | ||
if (typeof(_this[name]) !== 'undefined') { | ||
return _this[name]; | ||
@@ -39,27 +129,261 @@ } | ||
// Catch the WS methods calls | ||
var handler = handlerMaker(); | ||
handler.ws = name; | ||
handler.get = function (target, name) { | ||
var ws = this.ws; | ||
return { | ||
call: function (params, callback) { | ||
return _this.call(ws, name, params, callback); | ||
} | ||
// Catch WS method name | ||
if (_this.wsList[name].type === 'WS') { | ||
var handler = handlerMaker(); | ||
handler.$ws = name; | ||
handler.get = function (target, name) { | ||
var ws = this.$ws; | ||
return { | ||
call: function (params, callback) { | ||
return _this.callWS(ws, name, params, callback); | ||
} | ||
}; | ||
}; | ||
}; | ||
return new Proxy.create(handler); | ||
return new Proxy.create(handler); | ||
} | ||
else { | ||
return _this.proxyREST(target, name); | ||
} | ||
}; | ||
return new Proxy.create(handler); | ||
} | ||
}; | ||
// Call a ws method | ||
OVHWS.prototype.call = function (ws, method, params, callback) { | ||
OVHWS.prototype.proxyREST = function (target, name, reuseHandler) { | ||
var _this = this, | ||
handler = handlerMaker(); | ||
// Root | ||
if (typeof(target.wsList) !== 'undefined') { | ||
handler.$api = name; | ||
handler.$path = [name]; | ||
} | ||
// Sub level | ||
else { | ||
if (typeof(reuseHandler) !== 'undefined' && reuseHandler) { | ||
handler = target; | ||
} | ||
else { | ||
handler = handlerMaker(target); | ||
handler.$api = target.$api; | ||
handler.$path = []; | ||
for (var i = 0 ; i < target.$path.length ; ++i) { | ||
handler.$path.push(target.$path[i]); | ||
} | ||
} | ||
if (typeof(name) !== 'undefined') { | ||
handler.$path.push(name); | ||
} | ||
} | ||
handler.$params = {}; | ||
handler.get = function (target, name) { | ||
var _handler = this; | ||
// Existing | ||
if (typeof(_this[name]) !== 'undefined') { | ||
return _this[name]; | ||
} | ||
if (typeof(this.$value) !== 'undefined' && typeof(this.$value[name]) !== 'undefined') { | ||
return this.$value[name]; | ||
} | ||
// Action | ||
if (typeof(this.wsList) === 'undefined' && name === 'call' || name === '$call') { | ||
return function (httpMethod, path, params, callback) { | ||
_this.callREST(_handler.$api, httpMethod, path, | ||
typeof(params) === 'function' ? {} : params, | ||
typeof(params) === 'function' ? params : callback); | ||
}; | ||
} | ||
else if (name.charAt(0) === '$') { | ||
var strippedName = name.substring(1); | ||
if (_this.actionsREST.hasOwnProperty(strippedName)) { | ||
return function (params, callback) { | ||
// Already set parameters | ||
if (typeof(params) !== 'function') { | ||
for (var key in params) { | ||
if (params.hasOwnProperty(key)) { | ||
_handler.$params[key] = params[key]; | ||
} | ||
} | ||
} | ||
_this.callREST(_handler.$api, _this.actionsREST[strippedName], | ||
'/' + _handler.$path.join('/'), | ||
_handler.$params, | ||
typeof(params) === 'function' ? params : callback, | ||
_handler); | ||
}; | ||
} | ||
} | ||
else { | ||
return _this.proxyREST(this, name); | ||
} | ||
}; | ||
handler.set = function (target, name, value) { | ||
this.$params[name] = value; | ||
}; | ||
return Proxy.create(handler); | ||
}; | ||
OVHWS.prototype.proxyResponseHandlerREST = function (refer, object) { | ||
if (typeof(object) !== 'object' || object === null) { | ||
return object; | ||
} | ||
var handler = handlerMaker(object); | ||
handler.$value = object; | ||
handler.$api = refer.$api; | ||
handler.$path = []; | ||
for (var i = 0 ; i < refer.$path.length ; ++i) { | ||
handler.$path.push(refer.$path[i]); | ||
} | ||
return this.proxyREST(handler, undefined, true); | ||
}; | ||
OVHWS.prototype.proxyResponseREST = function (response, refer, callback) { | ||
if (Object.prototype.toString.call(response) === '[object Array]') { | ||
var result = []; | ||
for (var i = 0 ; i < response.length ; ++i) { | ||
result.push(this.proxyResponseHandlerREST(refer, response[i], response[i])); | ||
} | ||
callback.call(this.proxyResponseHandlerREST(refer, response), true, result); | ||
} | ||
else { | ||
var proxy = this.proxyResponseHandlerREST(refer, response); | ||
callback.call(proxy, true, proxy); | ||
} | ||
}; | ||
OVHWS.prototype.setClientCredentials = function (consumerKey, credentialToken) { | ||
this.credentialToken = credentialToken; | ||
this.consumerKey = consumerKey; | ||
}; | ||
// Call REST API | ||
OVHWS.prototype.callREST = function (apiName, httpMethod, path, params, callback, refer) { | ||
if (apiName !== 'auth' && (typeof(this.apiKeys.credentialToken) !== 'string' || | ||
typeof(this.apiKeys.consumerKey) !== 'string')) { | ||
throw new Error('OVH API: No consumerKey / credentialToken defined.'); | ||
} | ||
// Replace "{str}", used for $call() | ||
if (path.indexOf('{') >= 0) { | ||
var newPath = path; | ||
for (var paramKey in params) { | ||
if (params.hasOwnProperty(paramKey)) { | ||
newPath = path.replace('{' + paramKey + '}', params[paramKey]); | ||
// Remove from body parameters | ||
if (newPath !== path) { | ||
delete params[paramKey]; | ||
} | ||
path = newPath; | ||
} | ||
} | ||
} | ||
var options = { | ||
host: this.wsServer, | ||
host: this.wsList[apiName].host, | ||
port: 443, | ||
method: httpMethod, | ||
path: this.wsList[apiName].basePath + path | ||
}; | ||
// Headers | ||
options.headers = { | ||
'Content-Type': 'application/json', | ||
'X-Ovh-Application': this.apiKeys.appKey, | ||
}; | ||
if (typeof(params) === 'object' && Object.keys(params).length > 0) { | ||
options.headers['Content-Length'] = JSON.stringify(params).length; // Enjoy 500 on chunked requests... | ||
} | ||
// Sign request | ||
if (apiName !== 'auth') { | ||
options.headers['X-Ovh-Consumer'] = this.apiKeys.consumerKey; | ||
options.headers['X-Ovh-Timestamp'] = Math.round(Date.now() / 1000); | ||
options.headers['X-Ovh-Signature'] = this.signREST(httpMethod, 'https://' + options.host + options.path, | ||
params, options.headers['X-Ovh-Timestamp']); | ||
} | ||
var _this = this; | ||
var req = https.request(options, function (res) { | ||
var body = ''; | ||
res.on('data', function (chunk) { | ||
body += chunk; | ||
}); | ||
res.on('end', function () { | ||
var response; | ||
try { | ||
response = JSON.parse(body); | ||
} catch (e) { | ||
if (res.statusCode !== 200) { | ||
callback(false, res.statusCode); | ||
} | ||
else { | ||
callback(false, 'Unable to parse JSON reponse'); | ||
} | ||
return false; | ||
} | ||
if (res.statusCode !== 200) { | ||
callback(false, res.statusCode, response.message); | ||
} | ||
else { | ||
// Return a proxy (for potential next request) | ||
if (typeof(refer) !== 'undefined') { | ||
_this.proxyResponseREST(response, refer, callback); | ||
} | ||
else { | ||
callback(true, response); | ||
} | ||
} | ||
}); | ||
}); | ||
req.on('error', function (e) { | ||
callback(false, e.errno); | ||
}); | ||
if (typeof(params) === 'object' && Object.keys(params).length > 0) { | ||
req.write(JSON.stringify(params)); | ||
} | ||
req.end(); | ||
}; | ||
// Sign a REST request | ||
OVHWS.prototype.signREST = function (httpMethod, url, params, timestamp) { | ||
var s = [ | ||
this.apiKeys.credentialToken, | ||
httpMethod, | ||
url, | ||
typeof(params) === 'object' && Object.keys(params).length > 0 ? JSON.stringify(params) : '', | ||
timestamp | ||
]; | ||
return '$1$' + crypto.createHash('sha1').update(s.join('+')).digest('hex'); | ||
}; | ||
// Call a WS | ||
OVHWS.prototype.callWS = function (ws, method, params, callback) { | ||
var options = { | ||
host: this.wsList[ws].host, | ||
port: 443, | ||
method: 'GET', | ||
path: '/' + this.wsList[ws] + '/rest.dispatcher/' + method + '?' + querystring.stringify({ params: JSON.stringify(params) }) | ||
path: '/' + this.wsList[ws].path + '/rest.dispatcher/' + method + '?' + querystring.stringify({ params: JSON.stringify(params) }) | ||
}; | ||
@@ -84,2 +408,3 @@ | ||
callback(false, 'Unable to parse JSON reponse'); | ||
return false; | ||
} | ||
@@ -111,8 +436,14 @@ | ||
var options = { | ||
host: _this.wsServer, | ||
host: _this.wsList[k].host, | ||
port: 443, | ||
method: 'GET', | ||
path: '/' + _this.wsList[k] + '/schema.json' | ||
method: 'GET' | ||
}; | ||
if (_this.wsList[k].type === 'REST') { | ||
options.path = (_this.wsList[k].basePath || '/') + _this.wsList[k].path + '.json'; | ||
} | ||
else { | ||
options.path = '/' + _this.wsList[k].path + '/schema.json'; | ||
} | ||
var _k = k; | ||
@@ -132,8 +463,3 @@ | ||
var wsMeta = JSON.parse(body); | ||
if (wsMeta.servicesInformations.prefix !== k) { | ||
callback(k, false, 'Your WS prefix should be ' + wsMeta.servicesInformations.prefix + ' instead of ' + k); | ||
} | ||
else { | ||
callback(k, true); | ||
} | ||
callback(k, true); | ||
_this.wsMetas[k] = wsMeta; | ||
@@ -158,8 +484,6 @@ }); | ||
module.exports = function (wsList, wsServer) { | ||
wsServer = wsServer || 'ws.ovh.com'; | ||
return new OVHWS(wsList, wsServer); | ||
module.exports = function (wsList, apiKeys) { | ||
return new OVHWS(wsList, apiKeys || {}); | ||
}; | ||
}).call(this); |
{ | ||
"name": "ovh", | ||
"version": "0.2.0", | ||
"description": "An helper library to use OVH web services", | ||
"version": "0.3.0", | ||
"description": "An helper library to use OVH web services and REST APIs", | ||
"homepage": "https://github.com/gierschv/node-ovh", | ||
@@ -27,4 +27,4 @@ "author": "Vincent Giersch <mail@vincent.sh>", | ||
"scripts": { | ||
"test": "make all" | ||
"test": "make no_auth_test" | ||
} | ||
} |
127
README.md
@@ -6,4 +6,5 @@ node-ovh | ||
node-ovh is a Node.js helper library for OVH web services. The module usage is similar to the frontend lib OVHWS-Wrapper. | ||
This module uses Node.js harmony proxies, so you have to use the --harmony-proxies flag while running node. | ||
node-ovh is a Node.js helper library for OVH web services and OVH REST APIs. | ||
The module usage is similar to the frontend lib OVHWS-Wrapper. | ||
This module uses Node.js harmony proxies, so you have to use the `--harmony-proxies` flag while running node. | ||
@@ -19,9 +20,11 @@ **This library is unofficial and consequently not maintained by OVH.** | ||
Example | ||
Examples | ||
-------- | ||
### Using OVH WS | ||
```javascript | ||
var ovh = require('ovh'); | ||
// Instance a new OVH WS with used WS. Keys must be the WS prefixes | ||
// Instance a new OVH WS with used WS. | ||
var Ows = ovh({ | ||
@@ -41,5 +44,116 @@ sessionHandler: 'sessionHandler/r4', | ||
### Using OVH REST API | ||
#### Authentication | ||
The authentication of the OVH REST API is similar to OAuth. You need the `appKey` | ||
and the `appSecret` of [your application](https://www.ovh.com/fr/cgi-bin/api/createApplication.cgi). | ||
In the unit tests, the `appKey` and `appSecret` are those of RICO, [the API documentation](https://api.ovh.com). | ||
To use an API like *vps*, you need to allow your application to access to your OVH account. | ||
To do that, you need to call the method `credential` of the API *auth*. For example, using this library: | ||
```javascript | ||
var ovh = require('ovh'); | ||
var rest = ovh({ | ||
auth: { type: 'REST', path: '/auth' } | ||
}, { | ||
appKey: 'YOUR_APP_KEY', | ||
appSecret: 'YOUR_APP_SECRET' | ||
}); | ||
rest.auth.call('POST', '/auth/credential', { | ||
'accessRules': [ | ||
{ 'method': 'GET', 'path': '/*'}, | ||
{ 'method': 'POST', 'path': '/*'}, | ||
{ 'method': 'PUT', 'path': '/*'}, | ||
{ 'method': 'DELETE', 'path': '/*'} | ||
] | ||
}, function (success, credential) { | ||
console.log(credential); | ||
}); | ||
``` | ||
Result: | ||
```bash | ||
$ node --harmony-proxies credential.js | ||
{ validationUrl: 'https://www.ovh.com/fr/cgi-bin/api/requestCredential.cgi?credentialToken=AAAAAAAAAAAAAAAAAAAAAAAAAA', | ||
consumerKey: 'BBBBBBBBBBBBBBBBBBBBB', | ||
state: 'pendingValidation' } | ||
``` | ||
To allow your application to use your account, you just need to go to the `validationUrl` and to authorize the application. | ||
After that, you will be able to use this `credentialToken` and `consumerKey`. For more information, read the examples below. | ||
#### Usage | ||
You can call the REST API using differents ways. The first is similar to the WS usage: | ||
```javascript | ||
var ovh = require('ovh'); | ||
// Construct | ||
var rest = ovh({ | ||
vps: { type: 'REST', path: '/vps' } | ||
}, { | ||
appKey: 'X', appSecret: 'Y', consumerKey: 'B', credentialToken: 'A' | ||
}); | ||
// Requesting "GET /vps" | ||
rest.vps.call('GET', '/vps', function (success, vps) { | ||
console.log(!success || vps); | ||
}); | ||
// Requesting "GET /vps/vpsXXXX.ovh.net/ips" | ||
rest.vps.call('GET', '/vps/{domain}/ips', { domain: 'vpsXXXX.ovh.net' }, function (success, ips) { | ||
console.log(!success || ips); | ||
}); | ||
// Requesting "PUT /vps/vpsXXXX.ovh.net/ips" | ||
var params = { domain: 'vpsXXXX.ovh.net', ipAddress: '127.0.0.1', reverse: 'vpsXXXX.ovh.net' }; | ||
rest.vps.call('PUT', '/vps/{domain}/ips/{ipAddress}', params, function (success, httpErrorCode) { | ||
console.log(success || httpErrorCode); | ||
}); | ||
``` | ||
The second way is designed to be faster to use: | ||
```javascript | ||
var ovh = require('ovh'); | ||
// Construct | ||
var rest = ovh({ | ||
vps: { type: 'REST', path: '/vps' } | ||
}, { | ||
appKey: 'X', appSecret: 'Y', consumerKey: 'B', credentialToken: 'A' | ||
}); | ||
// Requesting "GET /vps" | ||
rest.vps.get(function (success, vps) { | ||
console.log(!success || vps); | ||
}); | ||
// Requesting "GET /vps/vpsXXXX.ovh.net/ips", | ||
// "GET /vps/vpsXXXX.ovh.net/ips/{ip}" and "PUT /vps/vpsXXXX.ovh.net/ips/{ip}" for each IP | ||
rest.vps['vpsXXXX.ovh.net'].ips.$get(function (success, ips) { | ||
for (var i = 0 ; i < ips.length ; ++i) { | ||
this[ips[i]].$get(function (success, ipDetails) { | ||
ipDetails.reverse = 'my-reverse.example.com'; | ||
ipDetails.$put(); | ||
// Or could be: ipDetails.$put({ reverse: 'my-reverse.example.com' }); | ||
}); | ||
} | ||
}); | ||
``` | ||
More examples in *test* folder. | ||
Changelog | ||
--------- | ||
### 0.3.0 | ||
* Major update, first version supporting OVH REST API (https://api.ovh.com). | ||
### 0.2.0 | ||
@@ -65,3 +179,3 @@ | ||
``` | ||
Copyright (c) 2012 Vincent Giersch | ||
Copyright (c) 2012 - 2013 Vincent Giersch <mail@vincent.sh> | ||
@@ -74,2 +188,3 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.``` | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
``` |
@@ -12,7 +12,6 @@ // | ||
// test.expect(3); | ||
var remaining = 3; | ||
var remaining = 4; | ||
var Ows = ovh({ | ||
sessionHandler: 'sessionHandler/r4', | ||
sessionHandlerEU: { type: 'WS', path: 'sessionHandler/r4' }, | ||
sessionHandlerCA: { type: 'WS', path: 'sessionHandler/r4', host: 'ws.ovh.ca' }, | ||
cloudInstance: 'cloud/public/instance/r3', | ||
@@ -41,15 +40,3 @@ xdsl: 'xdsl/trunk' | ||
}); | ||
}, | ||
'checkWSBadPrefix': function (done) { | ||
"use strict"; | ||
var Ows = ovh({ | ||
adsl: 'xdsl/trunk' | ||
}); | ||
Ows.checkWS(function (wsPrefix, success, errorMsg) { | ||
assert.ok(!success, 'Test WS ' + wsPrefix); | ||
done(); | ||
}); | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42226
13
817
186
1
2