Created Version Class

The version class allows us to parse, and compare, API version strings
as per the OpenStack API WG Microversion Specification. Several formats
are supported:

* v#.#.# (legacy)
* #.#.# (legacy)
* [service name] #.#.#

In the former two cases, a service type will have to be manually supplied
to ensure proper comparison.

https://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html

Change-Id: If3596418dfe2e76489bdb48d4d15f7f3e7a6d2f9
This commit is contained in:
Michael Krotscheck 2016-08-11 13:48:02 -07:00
parent f9fb75b32f
commit df77f84ecb
3 changed files with 215 additions and 1 deletions

View File

@ -2,7 +2,7 @@ verbose: false
instrumentation:
root: .
includes:
- 'src/**'
- 'src/**/*'
default-excludes: true
reporting:
print: detail

115
src/util/version.js Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the specific language governing permissions and limitations
* under the License.
*/
/**
* A simple version parser, able to parse various version strings used in OpenStack into a
* 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;
}
/**
* The major version.
*
* @returns {Number} The major version number
*/
get major () {
return this._major || 0;
}
/**
* The minor version.
*
* @returns {Number} The minor version number
*/
get minor () {
return this._minor || 0;
}
/**
* The patch version.
*
* @returns {Number} The patch version number.
*/
get patch () {
return this._patch || 0;
}
/**
* Create a new instance of a service version.
*
* @param {String} service The name of the service.
* @param {String} versionString The version string for this service.
*/
constructor (service, versionString) {
// Sanitize input
if (typeof service !== 'string') {
service = undefined;
}
if (typeof versionString !== 'string') {
versionString = undefined;
}
if (versionString === undefined) {
versionString = service;
} else {
this._service = service;
}
// Sanity check before running regex.
if (!versionString || !versionString.match) {
return;
}
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);
}
}
/**
* Compare this instance to another instance or version string.
*
* @param {String|Version} version The version to compare to.
* @returns {boolean} True if they are exactly the same, otherwise false.
*/
equals (version) {
if (!(version instanceof Version)) {
// is it a parseable string?
if (typeof version === 'string') {
version = new Version(version);
} else {
return false;
}
}
return version.major === this.major &&
version.minor === this.minor &&
version.patch === this.patch &&
version.service === this.service;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright (c) 2016 Hewlett Packard Enterprise Development L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
* the License for the specific language governing permissions and limitations
* under the License.
*/
import Version from '../../../src/util/version.js';
describe('Version', () => {
it("should parse various header versions", () => {
const testVersion = function (args, results) {
const v = new Version(...args);
expect(v.service).toBe(results[0]);
expect(v.major).toBe(results[1]);
expect(v.minor).toBe(results[2]);
expect(v.patch).toBe(results[3]);
};
testVersion(['identity 1.2'], ['identity', 1, 2, 0]);
testVersion(['identity 0.2'], ['identity', 0, 2, 0]);
testVersion(['compute 2222222.09'], ['compute', 2222222, 9, 0]);
testVersion(['compute 03.09'], ['compute', 3, 9, 0]);
testVersion(['compute 03.0'], ['compute', 3, 0, 0]);
testVersion(['compute 1'], ['compute', 1, 0, 0]);
testVersion(['compute 0'], ['compute', 0, 0, 0]);
testVersion(['v2.1.1'], [null, 2, 1, 1]);
testVersion(['v2.1'], [null, 2, 1, 0]);
testVersion(['v2'], [null, 2, 0, 0]);
testVersion(['v'], [null, 0, 0, 0]);
testVersion(['v0.2'], [null, 0, 2, 0]);
testVersion(['2.1.1'], [null, 2, 1, 1]);
testVersion(['2.1'], [null, 2, 1, 0]);
testVersion(['2'], [null, 2, 0, 0]);
testVersion([''], [null, 0, 0, 0]);
testVersion(['0.2'], [null, 0, 2, 0]);
testVersion(['compute', 'v2.1.1'], ['compute', 2, 1, 1]);
testVersion(['compute', 'v2.1'], ['compute', 2, 1, 0]);
testVersion(['compute', 'v2'], ['compute', 2, 0, 0]);
testVersion(['compute', 'v'], ['compute', 0, 0, 0]);
testVersion(['compute', 'v0.2'], ['compute', 0, 2, 0]);
testVersion(['compute', '2.1.1'], ['compute', 2, 1, 1]);
testVersion(['compute', '2.1'], ['compute', 2, 1, 0]);
testVersion(['compute', '2'], ['compute', 2, 0, 0]);
testVersion(['compute', ''], ['compute', 0, 0, 0]);
testVersion(['compute', '0.2'], ['compute', 0, 2, 0]);
// Invalid inputs...
testVersion([null, null], [null, 0, 0, 0]);
testVersion([{}, {}], [null, 0, 0, 0]);
testVersion([null, {}], [null, 0, 0, 0]);
testVersion([{}, null], [null, 0, 0, 0]);
});
it("should test for correct equality", () => {
const v1 = new Version("compute", "1.0.0");
// String tests...
expect(v1.equals("compute 1.0.0")).toBe(true);
expect(v1.equals("compute 1.0.1")).toBe(false);
expect(v1.equals("identity 1.0.0")).toBe(false);
// Version tests
expect(v1.equals(new Version("compute 1.0.0"))).toBe(true);
expect(v1.equals(new Version("compute 1.0.1"))).toBe(false);
expect(v1.equals(new Version("identity 1.0.0"))).toBe(false);
expect(v1.equals(new Version("1.0.0"))).toBe(false);
// Other tests...
expect(v1.equals({})).toBe(false);
const v2 = new Version("1.0.0");
// String tests...
expect(v2.equals("compute 1.0.0")).toBe(false);
expect(v2.equals("compute 1.0.1")).toBe(false);
expect(v2.equals("1.0.0")).toBe(true);
// Version tests
expect(v2.equals(new Version("compute 1.0.0"))).toBe(false);
expect(v2.equals(new Version("compute 1.0.1"))).toBe(false);
expect(v2.equals(new Version("identity 1.0.0"))).toBe(false);
expect(v2.equals(new Version("1.0.0"))).toBe(true);
// Other tests...
expect(v2.equals({})).toBe(false);
});
});