Update eslint and switch to standard

The openstack eslint package is just really not needed at this
point. Use standardjs and run --fix on it.

Switch away from using node 8 to run linters and tests.

Change-Id: I51f68d4ec30c8da5d1601ef00045e53f90d0554b
changes/44/730644/7
Monty Taylor 3 years ago
parent 7f843d7ecb
commit 346f7eeda0

@ -1,6 +1,16 @@
extends: openstack/es2015
parser: babel-eslint
plugins:
- standard
rules:
space-before-function-paren:
- 2
- never
no-var: error
no-console: off
semi: [error, never]
quotes: [error, single]
lines-between-class-members: error
extends:
- eslint:recommended
env:
browser: true
es6: true
jasmine: true
node: true

@ -1,27 +1,26 @@
/*eslint no-process-env: "off" */
import fs from 'fs';
import karma from 'karma/lib/config';
import path from 'path';
/* eslint no-process-env: "off" */
import fs from 'fs'
import karma from 'karma/lib/config'
import path from 'path'
function getDevstackConfig() {
const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js'));
function getDevstackConfig () {
const karmaConfig = karma.parseConfig(path.resolve('./karma.conf.js'))
return getCorsConfig('$KEYSTONE_CONF', karmaConfig) +
getCorsConfig('$GLANCE_API_CONF', karmaConfig) +
getCorsConfig('$NEUTRON_CONF', karmaConfig) +
getCorsConfig('$NOVA_CONF', karmaConfig);
getCorsConfig('$NOVA_CONF', karmaConfig)
}
function getCorsConfig(service, karmaConfig) {
function getCorsConfig (service, karmaConfig) {
return `[[post-config|${service}]]
[cors]
allowed_origin=http://localhost:${karmaConfig.port}
`;
`
}
fs.appendFile(process.env.BASE + '/new/devstack/local.conf', getDevstackConfig(), (err) => {
if (err) {
throw err;
throw err
}
});
})

@ -1,9 +1,9 @@
import webpackConfig from './webpack.config.babel';
import path from 'path';
import webpackConfig from './webpack.config.babel'
import path from 'path'
export default (config) => {
// test mode based on basePath parameter (eg. test/unit, test/functional)
const testDir = config.basePath ? path.basename(config.basePath) : 'unit';
const testDir = config.basePath ? path.basename(config.basePath) : 'unit'
config.set({
@ -69,9 +69,9 @@ export default (config) => {
// Generate a coverage report in /cover/karma
coverageReporter: {
type: 'html', //produces a html document after code is run
dir: '../../cover/' + testDir + '/browser/' //path to created html doc
},
type: 'html', // produces a html document after code is run
dir: '../../cover/' + testDir + '/browser/' // path to created html doc
}
});
};
})
}

@ -1,2 +1,2 @@
require('@babel/register');
module.exports = require('./karma.conf.babel').default;
require('@babel/register')
module.exports = require('./karma.conf.babel').default

@ -41,11 +41,12 @@
"@babel/preset-env": "^7.0.0",
"@babel/register": "^7.0.0",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.0.0",
"babel-plugin-transform-inline-environment-variables": "^6.8.0",
"eslint": "^3.0.0",
"eslint-config-openstack": "4.0.1",
"eslint": "^7.1.0",
"eslint-plugin-standard": "^4.0.1",
"fetch-mock": "^5.0.5",
"jasmine": "^3.5.0",
"js-yaml": "^3.14.0",

@ -13,7 +13,7 @@
* the License for the specific language governing permissions and limitations
* under the License.
*/
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -23,10 +23,9 @@ import AbstractService from './util/abstractService';
*/
const supportedGlanceVersions = [
'v2.3'
];
]
export default class Glance extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Glance API of a specific
* cloud. The constructor requires that you provide a specific glance interface endpoint
@ -42,17 +41,17 @@ export default class Glance extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedGlanceVersions);
this._config = endpointConfig;
super(endpointConfig.url, supportedGlanceVersions)
this._config = endpointConfig
}
/**
@ -62,11 +61,11 @@ export default class Glance extends AbstractService {
* into one.
* @returns {Promise.<T>} A promise which will resolve with the list of images.
*/
imageList(token = null) {
imageList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}images`, headers))
.then((response) => response.json())
.then((body) => body.images);
.then((body) => body.images)
}
}

@ -1,4 +1,4 @@
export {default as Keystone} from './keystone';
export {default as Glance} from './glance';
export {default as Neutron} from './neutron';
export {default as OpenStack} from './openstack';
export { default as Keystone } from './keystone'
export { default as Glance } from './glance'
export { default as Neutron } from './neutron'
export { default as OpenStack } from './openstack'

@ -1,4 +1,4 @@
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -8,10 +8,9 @@ import AbstractService from './util/abstractService';
*/
const supportedKeystoneVersions = [
'v3.1'
];
]
export default class Keystone extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Keystone API of a specific
* cloud. The constructor requires that you provide a configuration object for a specific
@ -22,17 +21,17 @@ export default class Keystone extends AbstractService {
* @param {{}} cloudConfig The configuration object for a specific cloud.
* @see http://docs.openstack.org/developer/os-client-config/#site-specific-file-locations
*/
constructor(cloudConfig) {
constructor (cloudConfig) {
// Sanity checks.
if (!cloudConfig) {
throw new Error('A configuration is required.');
throw new Error('A configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
cloudConfig = Object.assign({}, cloudConfig);
cloudConfig = Object.assign({}, cloudConfig)
super(cloudConfig.auth.auth_url, supportedKeystoneVersions);
this.cloudConfig = cloudConfig;
super(cloudConfig.auth.auth_url, supportedKeystoneVersions)
this.cloudConfig = cloudConfig
}
/**
@ -44,18 +43,18 @@ export default class Keystone extends AbstractService {
* @returns {String} The value found in the config, or null.
* @ignore
*/
_safeConfigGet(path) {
let segments = path.split('.');
let pointer = this.cloudConfig;
_safeConfigGet (path) {
const segments = path.split('.')
let pointer = this.cloudConfig
while (segments.length > 0) {
let prop = segments.shift();
if (pointer.hasOwnProperty(prop)) {
pointer = pointer[prop];
const prop = segments.shift()
if (Object.prototype.hasOwnProperty.call(pointer, prop)) {
pointer = pointer[prop]
} else {
return null;
return null
}
}
return pointer;
return pointer
}
/**
@ -64,9 +63,9 @@ export default class Keystone extends AbstractService {
* @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected
*/
_rawVersions() {
_rawVersions () {
return super._rawVersions()
.then((versions) => versions.values);
.then((versions) => versions.values)
}
/**
@ -92,7 +91,7 @@ export default class Keystone extends AbstractService {
* Not required if a project ID is given.
* @returns {Promise.<T>} A promise which will resolve with a valid token.
*/
tokenIssue({
tokenIssue ({
user_id: userId = this._safeConfigGet('auth.user_id'),
username = this._safeConfigGet('auth.username'),
password = this._safeConfigGet('auth.password'),
@ -103,32 +102,32 @@ export default class Keystone extends AbstractService {
project_domain_id: projectDomainId = this._safeConfigGet('auth.project_domain_id'),
project_domain_name: projectDomainName = this._safeConfigGet('auth.project_domain_name')
} = {}) {
let project;
let user = {password};
let project
const user = { password }
if (userId) {
user.id = userId;
user.id = userId
} else if (username) {
user.name = username;
user.name = username
if (userDomainId) {
user.domain = {id: userDomainId};
user.domain = { id: userDomainId }
} else if (userDomainName) {
user.domain = {name: userDomainName};
user.domain = { name: userDomainName }
} else {
user.domain = {id: 'default'};
user.domain = { id: 'default' }
}
}
if (projectId) {
project = {id: projectId};
project = { id: projectId }
} else if (projectName) {
project = {name: projectName};
project = { name: projectName }
if (projectDomainId) {
project.domain = {id: projectDomainId};
project.domain = { id: projectDomainId }
} else if (projectDomainName) {
project.domain = {name: projectDomainName};
project.domain = { name: projectDomainName }
} else {
project.domain = {id: 'default'};
project.domain = { id: 'default' }
}
}
@ -136,18 +135,18 @@ export default class Keystone extends AbstractService {
auth: {
identity: {
methods: ['password'],
password: {user}
password: { user }
},
scope: project ? {project} : 'unscoped'
scope: project ? { project } : 'unscoped'
}
};
}
return this
.serviceEndpoint()
.then((url) => this.http.httpPost(`${url}auth/tokens`, body))
.then((response) => {
return response.headers.get('X-Subject-Token');
});
return response.headers.get('X-Subject-Token')
})
}
/**
@ -157,16 +156,16 @@ export default class Keystone extends AbstractService {
* @param {String} adminToken An optional admin token.
* @returns {Promise.<T>} A promise which will resolve if the token has been successfully revoked.
*/
tokenRevoke(token, adminToken = null) {
tokenRevoke (token, adminToken = null) {
return Promise
.all([this.serviceEndpoint(), token, adminToken])
.then(([url, token, adminToken]) => {
return [url, {
'X-Subject-Token': token,
'X-Auth-Token': adminToken || token
}];
}]
})
.then(([url, headers]) => this.http.httpRequest('DELETE', `${url}auth/tokens`, headers));
.then(([url, headers]) => this.http.httpRequest('DELETE', `${url}auth/tokens`, headers))
}
/**
@ -175,17 +174,17 @@ export default class Keystone extends AbstractService {
* @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with information about the token.
*/
tokenInfo(token) {
tokenInfo (token) {
return Promise
.all([this.serviceEndpoint(), token])
.then(([url, token]) => {
return [url, {
'X-Subject-Token': token,
'X-Auth-Token': token
}];
}]
})
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/tokens`, headers))
.then((response) => response.json());
.then((response) => response.json())
}
/**
@ -194,11 +193,11 @@ export default class Keystone extends AbstractService {
* @param {String} token The authorization token.
* @returns {Promise.<T>} A promise which will resolve with the service catalog.
*/
catalogList(token = null) {
catalogList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}auth/catalog`, headers))
.then((response) => response.json())
.then((body) => body.catalog);
.then((body) => body.catalog)
}
}

@ -13,7 +13,7 @@
* the License for the specific language governing permissions and limitations
* under the License.
*/
import AbstractService from './util/abstractService';
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -23,10 +23,9 @@ import AbstractService from './util/abstractService';
*/
const supportedNeutronVersions = [
'v2.0'
];
]
export default class Neutron extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Neutron API of a specific
* cloud. The constructor requires that you provide a specific neutron interface endpoint
@ -42,16 +41,16 @@ export default class Neutron extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific glance endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedNeutronVersions);
super(endpointConfig.url, supportedNeutronVersions)
}
/**
@ -60,11 +59,11 @@ export default class Neutron extends AbstractService {
* @param {String} token An authorization token, or a promise which will resolve into one.
* @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/
networkList(token = null) {
networkList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}/networks`, headers))
.then((response) => response.json())
.then((body) => body.networks);
.then((body) => body.networks)
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
import AbstractService from "./util/abstractService";
import AbstractService from './util/abstractService'
/**
* A list of all supported versions. Please keep this array sorted by most recent.
@ -24,10 +24,9 @@ import AbstractService from "./util/abstractService";
*/
const supportedNovaVersions = [
'v2.1'
];
]
export default class Nova extends AbstractService {
/**
* This class provides direct, idempotent, low-level access to the Nova API of a specific
* cloud. The constructor requires that you provide a specific nova interface endpoint
@ -43,17 +42,17 @@ export default class Nova extends AbstractService {
* }
* @param {{}} endpointConfig The configuration element for a specific nova endpoint.
*/
constructor(endpointConfig) {
constructor (endpointConfig) {
// Sanity checks.
if (!endpointConfig || !endpointConfig.url) {
throw new Error('An endpoint configuration is required.');
throw new Error('An endpoint configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
endpointConfig = Object.assign({}, endpointConfig);
endpointConfig = Object.assign({}, endpointConfig)
super(endpointConfig.url, supportedNovaVersions);
this._config = endpointConfig;
super(endpointConfig.url, supportedNovaVersions)
this._config = endpointConfig
}
/**
@ -62,11 +61,11 @@ export default class Nova extends AbstractService {
* @param {String} token An authorization token, or a promise which will resolve into one.
* @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/
flavorList(token = null) {
flavorList (token = null) {
return this
._requestComponents(token)
.then(([url, headers]) => this.http.httpRequest('GET', `${url}flavors`, headers))
.then((response) => response.json())
.then((body) => body.flavors);
.then((body) => body.flavors)
}
}

@ -1,7 +1,7 @@
import Keystone from "./keystone";
import Neutron from "./neutron";
import Glance from "./glance";
import Nova from "./nova";
import Keystone from './keystone'
import Neutron from './neutron'
import Glance from './glance'
import Nova from './nova'
export default class OpenStack {
/**
@ -9,21 +9,21 @@ export default class OpenStack {
*
* @param {{}} cloudConfig The configuration object for a specific cloud.
*/
constructor(cloudConfig) {
constructor (cloudConfig) {
// Sanity checks.
if (!cloudConfig) {
throw new Error('A configuration is required.');
throw new Error('A configuration is required.')
}
// Clone the config, so that this instance is immutable
// at runtime (no modifying the config after the fact).
cloudConfig = Object.assign({}, cloudConfig);
cloudConfig = Object.assign({}, cloudConfig)
this.cloudConfig = cloudConfig;
this.cloudConfig = cloudConfig
}
getConfig() {
getConfig () {
// Returns the config instance
return this.cloudConfig;
return this.cloudConfig
}
/**
@ -31,9 +31,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of networks.
*/
networkList() {
networkList () {
return this._neutron
.then((neutron) => neutron.networkList(this._token));
.then((neutron) => neutron.networkList(this._token))
}
/**
@ -41,9 +41,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of images.
*/
imageList() {
imageList () {
return this._glance
.then((glance) => glance.imageList(this._token));
.then((glance) => glance.imageList(this._token))
}
/**
@ -51,9 +51,9 @@ export default class OpenStack {
*
* @returns {Promise.<T>} A promise which will resolve with the list of flavors.
*/
flavorList() {
flavorList () {
return this._nova
.then((nova) => nova.flavorList(this._token));
.then((nova) => nova.flavorList(this._token))
}
/**
@ -62,12 +62,12 @@ export default class OpenStack {
* @returns {Promise.<Keystone>} A promise which will resolve with Keystone instance.
* @private
*/
get _keystone() {
get _keystone () {
if (!this._keystonePromise) {
this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()));
this._keystonePromise = Promise.resolve(new Keystone(this.getConfig()))
}
return this._keystonePromise;
return this._keystonePromise
}
/**
@ -76,12 +76,12 @@ export default class OpenStack {
* @returns {Promise.<Neutron>} A promise which will resolve with Neutron instance.
* @private
*/
get _neutron() {
get _neutron () {
if (!this._neutronPromise) {
this._neutronPromise = this._getComponentConfigFor('neutron')
.then((componentConfig) => new Neutron(componentConfig));
.then((componentConfig) => new Neutron(componentConfig))
}
return this._neutronPromise;
return this._neutronPromise
}
/**
@ -90,12 +90,12 @@ export default class OpenStack {
* @returns {Promise.<Glance>} A promise which will resolve with Glance instance.
* @private
*/
get _glance() {
get _glance () {
if (!this._glancePromise) {
this._glancePromise = this._getComponentConfigFor('glance')
.then((componentConfig) => new Glance(componentConfig));
.then((componentConfig) => new Glance(componentConfig))
}
return this._glancePromise;
return this._glancePromise
}
/**
@ -104,12 +104,12 @@ export default class OpenStack {
* @returns {Promise.<Nova>} A promise which will resolve with Nova instance.
* @private
*/
get _nova() {
get _nova () {
if (!this._novaPromise) {
this._novaPromise = this._getComponentConfigFor('nova')
.then((componentConfig) => new Nova(componentConfig));
.then((componentConfig) => new Nova(componentConfig))
}
return this._novaPromise;
return this._novaPromise
}
/**
@ -118,11 +118,11 @@ export default class OpenStack {
* @returns {Promise.<T>} A promise which will resolve with the token.
* @private
*/
get _token() {
get _token () {
if (!this._tokenPromise) {
this._tokenPromise = this._keystone.then((k) => k.tokenIssue());
this._tokenPromise = this._keystone.then((k) => k.tokenIssue())
}
return this._tokenPromise;
return this._tokenPromise
}
/**
@ -132,14 +132,13 @@ export default class OpenStack {
* @returns {Promise.<{}>} A promise which will resolve with the component config.
* @private
*/
_getComponentConfigFor(name) {
const config = this.getConfig();
_getComponentConfigFor (name) {
const config = this.getConfig()
return this._token
.then((token) => this._keystone.then((keystone) => keystone.catalogList(token)))
.then((catalog) => catalog.find((entry) => entry.name === name))
.then((entry) => entry.endpoints.find((endpoint) => {
return endpoint.region === config.region_name && endpoint.interface === 'public';
}));
return endpoint.region === config.region_name && endpoint.interface === 'public'
}))
}
}

@ -14,12 +14,11 @@
* under the License.
*/
import Http from './http';
import Version from './version';
import URL from 'url-parse';
import Http from './http'
import Version from './version'
import URL from 'url-parse'
export default class AbstractService {
/**
* This class provides an abstract implementation of our services, which includes logic common to
* all of our services.
@ -27,9 +26,9 @@ export default class AbstractService {
* @param {string} endpointUrl The endpoint URL.
* @param {Array} supportedVersions The list of all supported versions.
*/
constructor(endpointUrl, supportedVersions) {
this._endpointUrl = endpointUrl;
this._supportedVersions = supportedVersions;
constructor (endpointUrl, supportedVersions) {
this._endpointUrl = endpointUrl
this._supportedVersions = supportedVersions
}
/**
@ -37,11 +36,11 @@ export default class AbstractService {
*
* @returns {Http} Our HTTP service instance.
*/
get http() {
get http () {
if (!this._http) {
this._http = new Http();
this._http = new Http()
}
return this._http;
return this._http
}
/**
@ -49,8 +48,8 @@ export default class AbstractService {
*
* @returns {Array} The list of all supported versions, or empty array.
*/
get supportedVersions() {
return this._supportedVersions || [];
get supportedVersions () {
return this._supportedVersions || []
}
/**
@ -58,8 +57,8 @@ export default class AbstractService {
*
* @returns {string} The URL of our service.
*/
get endpointUrl() {
return this._endpointUrl;
get endpointUrl () {
return this._endpointUrl
}
/**
@ -67,14 +66,14 @@ export default class AbstractService {
*
* @returns {Promise.<Version[]>} A promise that will resolve with the list of API versions.
*/
versions() {
versions () {
return this._rawVersions().then((versions) => {
return versions.map((rawVersion) => {
const version = new Version(rawVersion.id);
version.links = rawVersion.links;
return version;
});
});
const version = new Version(rawVersion.id)
version.links = rawVersion.links
return version
})
})
}
/**
@ -83,26 +82,26 @@ export default class AbstractService {
* @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
* @protected
*/
_rawVersions() {
_rawVersions () {
return new Promise((resolve, reject) => {
this.http
.httpGet(this._endpointUrl)
.catch((response) => {
if (response.status === 401) {
let rootUrl = new URL(this._endpointUrl);
rootUrl.set('pathname', '/');
rootUrl.set('query', '');
rootUrl.set('hash', '');
const rootUrl = new URL(this._endpointUrl)
rootUrl.set('pathname', '/')
rootUrl.set('query', '')
rootUrl.set('hash', '')
return this.http.httpGet(rootUrl.href);
return this.http.httpGet(rootUrl.href)
} else {
throw response;
throw response
}
})
.then((response) => response.json())
.then((body) => resolve(body.versions))
.catch(reject);
});
.catch(reject)
})
}
/**
@ -110,17 +109,17 @@ export default class AbstractService {
*
* @returns {Promise.<Version>} A promise that will resolve with the specific API version.
*/
version() {
version () {
return this
.versions()
.then((versions) => {
for (let version of versions) {
for (const version of versions) {
if (this.supportedVersions.find(version.supports)) {
return version;
return version
}
}
throw new Error("No supported API version available.");
});
throw new Error('No supported API version available.')
})
}
/**
@ -128,22 +127,22 @@ export default class AbstractService {
*
* @returns {Promise.<T>|*} A promise which will resolve with the endpoint URL string.
*/
serviceEndpoint() {
serviceEndpoint () {
if (!this._endpointPromise) {
this._endpointPromise = this.version()
.then((version) => {
if (version.links) {
for (let i = 0; i < version.links.length; i++) {
let link = version.links[i];
const link = version.links[i]
if (link.rel === 'self' && link.href) {
return link.href;
return link.href
}
}
}
throw new Error("No service endpoint discovered.");
});
throw new Error('No service endpoint discovered.')
})
}
return this._endpointPromise;
return this._endpointPromise
}
/**
@ -155,18 +154,18 @@ export default class AbstractService {
* @returns {Promise} A promise which resolves with [url, token].
* @private
*/
_requestComponents(token = null) {
_requestComponents (token = null) {
// Make sure the token is a promise.
let headerPromise = Promise
const headerPromise = Promise
.resolve(token)
.then((token) => {
if (token) {
return {
'X-Auth-Token': token
};
}
}
return {};
});
return Promise.all([this.serviceEndpoint(), headerPromise]);
return {}
})
return Promise.all([this.serviceEndpoint(), headerPromise])
}
}

@ -14,8 +14,8 @@
* under the License.
*/
import 'isomorphic-fetch';
import log from 'loglevel';
import 'isomorphic-fetch'
import log from 'loglevel'
/**
* This utility class provides an abstraction layer for HTTP calls via fetch(). Its purpose is
@ -33,7 +33,6 @@ import log from 'loglevel';
* - Other features.
*/
export default class Http {
/**
* The default headers which will be sent with every request. A copy of these headers will be
* added to the Request instance passed through the interceptor chain, and may be
@ -41,18 +40,18 @@ export default class Http {
*
* @returns {{string: string}} A mapping of 'headerName': 'headerValue'
*/
get defaultHeaders() {
return this._defaultHeaders;
get defaultHeaders () {
return this._defaultHeaders
}
/**
* Create a new HTTP handler.
*/
constructor() {
constructor () {
// Add default response interceptors.
this._defaultHeaders = {
'Content-Type': 'application/json'
};
}
}
/**
@ -64,40 +63,39 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpRequest(method, url, headers = {}, body) {
httpRequest (method, url, headers = {}, body) {
// Sanitize the headers...
headers = Object.assign({}, headers, this.defaultHeaders);
headers = Object.assign({}, headers, this.defaultHeaders)
// Build the request
const init = {method, headers};
const init = { method, headers }
// The Request() constructor will throw an error if the method is GET/HEAD, and there's a body.
if (['GET', 'HEAD'].indexOf(method) === -1 && body) {
init.body = JSON.stringify(body);
init.body = JSON.stringify(body)
}
const request = new Request(url, init);
const request = new Request(url, init)
// Build the wrapper promise.
return new Promise((resolve, reject) => {
log.debug('-->', `HTTP ${method}`, url, JSON.stringify(headers), JSON.stringify(body));
let promise = fetch(request.url, init);
log.debug('-->', `HTTP ${method}`, url, JSON.stringify(headers), JSON.stringify(body))
const promise = fetch(request.url, init)
// Fetch will treat all http responses (2xx, 3xx, 4xx, 5xx, etc) as successful responses.
// This will catch all 4xx and 5xx responses and return them to the catch() handler. Note
// that it's up to the downstream developer to determine whether what they received is an
// error or a failed response.
promise.then((response) => {
log.debug('<--', `HTTP ${response.status}`);
log.debug('<--', `HTTP ${response.status}`)
if (response.status >= 400) {
return reject(response);
return reject(response)
} else {
return response;
return response
}
});
})
promise.then((response) => resolve(response), (error) => reject(error));
});
promise.then((response) => resolve(response), (error) => reject(error))
})
}
/**
@ -106,8 +104,8 @@ export default class Http {
* @param {String} url The request URL.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpGet(url) {
return this.httpRequest('GET', url, {}, null);
httpGet (url) {
return this.httpRequest('GET', url, {}, null)
}
/**
@ -117,8 +115,8 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpPut(url, body) {
return this.httpRequest('PUT', url, {}, body);
httpPut (url, body) {
return this.httpRequest('PUT', url, {}, body)
}
/**
@ -128,8 +126,8 @@ export default class Http {
* @param {{}} body The body. It will be JSON-Encoded by the handler.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpPost(url, body) {
return this.httpRequest('POST', url, {}, body);
httpPost (url, body) {
return this.httpRequest('POST', url, {}, body)
}
/**
@ -138,7 +136,7 @@ export default class Http {
* @param {String} url The request URL.
* @returns {Promise} A promise which will resolve with the processed request response.
*/
httpDelete(url) {
return this.httpRequest('DELETE', url, {}, null);
httpDelete (url) {
return this.httpRequest('DELETE', url, {}, null)
}
}

@ -19,14 +19,13 @@
* comparable instance.
*/
export default class Version {
/**
* The name of the service.
*
* @returns {String|*|null} The name of the service, or null.
*/
get service() {
return this._service || null;
get service () {
return this._service || null
}
/**
@ -34,8 +33,8 @@ export default class Version {
*
* @returns {Number} The major version number
*/
get major() {
return this._major || 0;
get major () {
return this._major || 0
}
/**
@ -43,8 +42,8 @@ export default class Version {
*
* @returns {Number} The minor version number
*/
get minor() {
return this._minor || 0;
get minor () {
return this._minor || 0
}
/**
@ -52,8 +51,8 @@ export default class Version {
*
* @returns {Number} The patch version number.
*/
get patch() {
return this._patch || 0;
get patch () {
return this._patch || 0
}
/**
@ -61,8 +60,8 @@ export default class Version {
*
* @returns {Object[]} The list of links.
*/
get links() {
return this._links || null;
get links () {
return this._links || null
}
/**
@ -70,9 +69,9 @@ export default class Version {
*
* @param {Object[]} links The links to be set
*/
set links(links) {
set links (links) {
if (Array.isArray(links)) {
this._links = links;
this._links = links
}
}
@ -82,37 +81,37 @@ export default class Version {
* @param {String} [service] The name of the service.
* @param {String} versionString The version string for this service.
*/
constructor(service, versionString) {
constructor (service, versionString) {
// Sanitize input
if (typeof service !== 'string') {
service = undefined;
service = undefined
}
if (typeof versionString !== 'string') {
versionString = undefined;
versionString = undefined
}
if (versionString === undefined) {
versionString = service;
versionString = service
} else {
this._service = service;
this._service = service
}
// Sanity check before running regex.
if (!versionString || !versionString.match) {
return;
return
}
const results = versionString.match(/^(([^ ]+) )?v?(([0-9]+)(\.([0-9]+)(.([0-9]+))?)?)$/);
const results = versionString.match(/^(([^ ]+) )?v?(([0-9]+)(\.([0-9]+)(.([0-9]+))?)?)$/)
if (results) {
this._service = results[2] || this._service; // regex takes precedence
this._major = parseInt(results[4], 10);
this._minor = parseInt(results[6], 10);
this._patch = parseInt(results[8], 10);
this._service = results[2] || this._service // regex takes precedence
this._major = parseInt(results[4], 10)
this._minor = parseInt(results[6], 10)
this._patch = parseInt(results[8], 10)
}
this._links = null;
this._links = null
this.equals = this.equals.bind(this);
this.supports = this.supports.bind(this);
this.equals = this.equals.bind(this)
this.supports = this.supports.bind(this)
}
/**
@ -121,20 +120,20 @@ export default class Version {
* @param {String|Version} version The version to compare to.
* @returns {boolean} True if they are exactly the same, otherwise false.
*/
equals(version) {
equals (version) {
if (!(version instanceof Version)) {
// is it a parseable string?
if (typeof version === 'string') {
version = new Version(version);
version = new Version(version)
} else {
return false;
return false
}
}
return version.major === this.major &&
version.minor === this.minor &&
version.patch === this.patch &&
version.service === this.service;
version.service === this.service
}
/**
@ -144,30 +143,30 @@ export default class Version {
* @param {String|Version} version the version to support.
* @returns {boolean} True if the version is compatible, otherwise false
*/
supports(version) {
supports (version) {
if (!(version instanceof Version)) {
if (typeof version === 'string') {
version = new Version(version);
version = new Version(version)
} else {
return false;
return false
}
}
const compatibleVersion = version.service === this.service &&
version.major === this.major &&
version.minor <= this.minor;
version.minor <= this.minor
if (compatibleVersion && version.minor === this.minor) {
return version.patch <= this.patch;
return version.patch <= this.patch
}
return compatibleVersion;
return compatibleVersion
}
toString() {
let version = `${this.major}.${this.minor}`;
toString () {
let version = `${this.major}.${this.minor}`
if (this.patch) {
version = `${version}.${this.patch}`;
version = `${version}.${this.patch}`
}
return version;
return version
}
}

@ -14,71 +14,69 @@
* under the License.
*/
import config from "./helpers/cloudsConfig";
import Version from '../../src/util/version';
import Glance from "../../src/glance";
import Keystone from "../../src/keystone";
import log from 'loglevel';
import config from './helpers/cloudsConfig'
import Version from '../../src/util/version'
import Glance from '../../src/glance'
import Keystone from '../../src/keystone'
import log from 'loglevel'
log.setLevel("DEBUG");
log.setLevel('DEBUG')
describe("Glance", () => {
describe('Glance', () => {
// Create a keystone instance and extract the glance API endpoint.
let devstackConfig = config.clouds.devstack;
let keystone = new Keystone(devstackConfig);
let tokenPromise = keystone.tokenIssue();
const devstackConfig = config.clouds.devstack
const keystone = new Keystone(devstackConfig)
const tokenPromise = keystone.tokenIssue()
let configPromise = tokenPromise
const configPromise = tokenPromise
.then((token) => keystone.catalogList(token))
.then((catalog) => catalog.find((entry) => entry.name === 'glance'))
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'));
.then((entry) => entry.endpoints.find((endpoint) => endpoint.interface === 'public'))
describe("versions()", () => {
it("should return a list of all versions available on this clouds' glance", (done) => {
describe('versions()', () => {
it('should return a list of all versions available on this clouds\' glance', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.versions())
.then((versions) => {
// Quick sanity check.
expect(versions.length > 0).toBeTruthy();
done();
expect(versions.length > 0).toBeTruthy()
done()
})
.catch((error) => done.fail(error));
});
});
.catch((error) => done.fail(error))
})
})
describe("version()", () => {
describe('version()', () => {
/**
* This test acts as a canary, to inform the SDK developers that the Glance API
* has changed in a significant way.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.version())
.then((apiVersion) => {
expect(apiVersion instanceof Version).not.toBeFalsy();
done();
expect(apiVersion instanceof Version).not.toBeFalsy()
done()
})
.catch((error) => done.fail(error));
});
});
describe("imageList()", () => {
.catch((error) => done.fail(error))
})
})
describe('imageList()', () => {
/**
* Assert that we can get a list of images.
*/
it("should return a supported version.", (done) => {
it('should return a supported version.', (done) => {
configPromise
.then((config) => new Glance(config))
.then((glance) => glance.imageList(tokenPromise))
.then((images) => {
expect(images.length > 0).toBeTruthy();
done();