Browse Source

Use serviceEndpoint compatible with versions

This patch adds a more flexible selection of version in AbstractService.
A version will be selected if it's compatible with the 'supportedVersions'.
Example:
  supportedVersions = ['v3.1'];
  All minor versions above 3.1 will be compatible (3.1.2, 3.2, 3.3 etc)
  However 4.x will not be compatible

Change-Id: Icd540449ebf6a09d9bb7e1d25a85e2dbe787c5a4
changes/35/400935/3
Corentin Ardeois 2 years ago
parent
commit
beec17c6e4

+ 6
- 5
src/keystone.js View File

@@ -7,7 +7,7 @@ import AbstractService from './util/abstractService';
7 7
  * @ignore
8 8
  */
9 9
 const supportedKeystoneVersions = [
10
-  'v3.7'
10
+  'v3.1'
11 11
 ];
12 12
 
13 13
 export default class Keystone extends AbstractService {
@@ -59,12 +59,13 @@ export default class Keystone extends AbstractService {
59 59
   }
60 60
 
61 61
   /**
62
-   * Retrieve all the API versions available.
62
+   * Retrieve all the raw API versions available.
63 63
    *
64
-   * @returns {Promise.<T>} A promise that will resolve with the list of API versions.
64
+   * @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
65
+   * @protected
65 66
    */
66
-  versions() {
67
-    return super.versions()
67
+  _rawVersions() {
68
+    return super._rawVersions()
68 69
       .then((versions) => versions.values);
69 70
   }
70 71
 

+ 20
- 3
src/util/abstractService.js View File

@@ -15,6 +15,7 @@
15 15
  */
16 16
 
17 17
 import Http from './http';
18
+import Version from './version';
18 19
 import URL from 'url-parse';
19 20
 
20 21
 export default class AbstractService {
@@ -64,9 +65,25 @@ export default class AbstractService {
64 65
   /**
65 66
    * Retrieve all the API versions available.
66 67
    *
67
-   * @returns {Promise.<T>} A promise that will resolve with the list of API versions.
68
+   * @returns {Promise.<Version[]>} A promise that will resolve with the list of API versions.
68 69
    */
69 70
   versions() {
71
+    return this._rawVersions().then((versions) => {
72
+      return versions.map((rawVersion) => {
73
+        const version = new Version(rawVersion.id);
74
+        version.links = rawVersion.links;
75
+        return version;
76
+      });
77
+    });
78
+  }
79
+
80
+  /**
81
+   * Retrieve all the raw API versions available.
82
+   *
83
+   * @returns {Promise.<Object[]>} A promise that will resolve with the list of raw versions.
84
+   * @protected
85
+   */
86
+  _rawVersions() {
70 87
     return new Promise((resolve, reject) => {
71 88
       let promise = this.http
72 89
         .httpGet(this._endpointUrl)
@@ -92,14 +109,14 @@ export default class AbstractService {
92 109
   /**
93 110
    * Retrieve the API version declaration that is currently in use by this instance.
94 111
    *
95
-   * @returns {Promise.<T>} A promise that will resolve with the specific API version.
112
+   * @returns {Promise.<Version>} A promise that will resolve with the specific API version.
96 113
    */
97 114
   version() {
98 115
     return this
99 116
       .versions()
100 117
       .then((versions) => {
101 118
         for (let version of versions) {
102
-          if (this.supportedVersions.indexOf(version.id) > -1) {
119
+          if (this.supportedVersions.find(version.supports)) {
103 120
             return version;
104 121
           }
105 122
         }

+ 59
- 1
src/util/version.js View File

@@ -56,10 +56,30 @@ export default class Version {
56 56
     return this._patch || 0;
57 57
   }
58 58
 
59
+  /**
60
+   * The links of the service
61
+   *
62
+   * @returns {Object[]} The list of links.
63
+   */
64
+  get links() {
65
+    return this._links || null;
66
+  }
67
+
68
+  /**
69
+   * Sets the links of the service
70
+   *
71
+   * @param {Object[]} links The links to be set
72
+   */
73
+  set links(links) {
74
+    if (Array.isArray(links)) {
75
+      this._links = links;
76
+    }
77
+  }
78
+
59 79
   /**
60 80
    * Create a new instance of a service version.
61 81
    *
62
-   * @param {String} service The name of the service.
82
+   * @param {String} [service] The name of the service.
63 83
    * @param {String} versionString The version string for this service.
64 84
    */
65 85
   constructor(service, versionString) {
@@ -89,6 +109,10 @@ export default class Version {
89 109
       this._minor = parseInt(results[6], 10);
90 110
       this._patch = parseInt(results[8], 10);
91 111
     }
112
+    this._links = null;
113
+
114
+    this.equals = this.equals.bind(this);
115
+    this.supports = this.supports.bind(this);
92 116
   }
93 117
 
94 118
   /**
@@ -112,4 +136,38 @@ export default class Version {
112 136
       version.patch === this.patch &&
113 137
       version.service === this.service;
114 138
   }
139
+
140
+  /**
141
+   * Verifies compatibility of this instance to another instance. Major version should be equal and
142
+   * minor version should be greater or equal than `version` parameter.
143
+   *
144
+   * @param {String|Version} version the version to support.
145
+   * @returns {boolean} True if the version is compatible, otherwise false
146
+   */
147
+  supports(version) {
148
+    if (!(version instanceof Version)) {
149
+      if (typeof version === 'string') {
150
+        version = new Version(version);
151
+      } else {
152
+        return false;
153
+      }
154
+    }
155
+
156
+    const compatibleVersion = version.service === this.service &&
157
+      version.major === this.major &&
158
+      version.minor <= this.minor;
159
+
160
+    if (compatibleVersion && version.minor === this.minor) {
161
+      return version.patch <= this.patch;
162
+    }
163
+    return compatibleVersion;
164
+  }
165
+
166
+  toString() {
167
+    let version = `${this.major}.${this.minor}`;
168
+    if (this.patch) {
169
+      version = `${version}.${this.patch}`;
170
+    }
171
+    return version;
172
+  }
115 173
 }

+ 3
- 7
test/functional/glanceTest.js View File

@@ -50,7 +50,7 @@ describe("Glance", () => {
50 50
   describe("version()", () => {
51 51
 
52 52
     const supportedApiVersions = [
53
-      new Version('image 2.3')
53
+      new Version('2.4')
54 54
     ];
55 55
 
56 56
     /**
@@ -61,11 +61,7 @@ describe("Glance", () => {
61 61
       configPromise
62 62
         .then((config) => new Glance(config))
63 63
         .then((glance) => glance.version())
64
-        .then((version) => {
65
-
66
-          // Quick sanity check.
67
-          const apiVersion = new Version('image', version.id);
68
-
64
+        .then((apiVersion) => {
69 65
           for (let i = 0; i < supportedApiVersions.length; i++) {
70 66
             let supportedVersion = supportedApiVersions[i];
71 67
             if (apiVersion.equals(supportedVersion)) {
@@ -73,7 +69,7 @@ describe("Glance", () => {
73 69
               return;
74 70
             }
75 71
           }
76
-          fail("Current devstack glance version is not supported.");
72
+          fail(`Current devstack glance version (${apiVersion}) is not supported.`);
77 73
           done();
78 74
         })
79 75
         .catch((error) => done.fail(error));

+ 3
- 7
test/functional/keystoneTest.js View File

@@ -43,7 +43,7 @@ describe("Keystone", () => {
43 43
   describe("version()", () => {
44 44
 
45 45
     const supportedApiVersions = [
46
-      new Version('identity 3.7')
46
+      new Version('3.7')
47 47
     ];
48 48
 
49 49
     /**
@@ -52,11 +52,7 @@ describe("Keystone", () => {
52 52
      */
53 53
     it("should return a supported version.", (done) => {
54 54
       keystone.version()
55
-        .then((version) => {
56
-
57
-          // Quick sanity check.
58
-          const apiVersion = new Version('identity', version.id);
59
-
55
+        .then((apiVersion) => {
60 56
           for (let i = 0; i < supportedApiVersions.length; i++) {
61 57
             let supportedVersion = supportedApiVersions[i];
62 58
             if (apiVersion.equals(supportedVersion)) {
@@ -64,7 +60,7 @@ describe("Keystone", () => {
64 60
               return;
65 61
             }
66 62
           }
67
-          fail("Current devstack keystone version is not supported.");
63
+          fail(`Current devstack keystone version (${apiVersion}) is not supported.`);
68 64
           done();
69 65
         })
70 66
         .catch((response) => response.json()

+ 3
- 7
test/functional/neutronTest.js View File

@@ -50,7 +50,7 @@ describe("neutron", () => {
50 50
   describe("version()", () => {
51 51
 
52 52
     const supportedApiVersions = [
53
-      new Version('network 2.0')
53
+      new Version('2.0')
54 54
     ];
55 55
 
56 56
     /**
@@ -61,11 +61,7 @@ describe("neutron", () => {
61 61
       configPromise
62 62
         .then((config) => new Neutron(config))
63 63
         .then((neutron) => neutron.version())
64
-        .then((version) => {
65
-
66
-          // Quick sanity check.
67
-          const apiVersion = new Version('network', version.id);
68
-
64
+        .then((apiVersion) => {
69 65
           for (let i = 0; i < supportedApiVersions.length; i++) {
70 66
             let supportedVersion = supportedApiVersions[i];
71 67
             if (apiVersion.equals(supportedVersion)) {
@@ -73,7 +69,7 @@ describe("neutron", () => {
73 69
               return;
74 70
             }
75 71
           }
76
-          fail("Current devstack neutron version is not supported.");
72
+          fail(`Current devstack neutron version (${apiVersion}) is not supported.`);
77 73
           done();
78 74
         })
79 75
         .catch((error) => done.fail(error));

+ 0
- 15
test/unit/glanceTest.js View File

@@ -31,21 +31,6 @@ describe('Glance', () => {
31 31
     expect(() => new Glance()).toThrow();
32 32
   });
33 33
 
34
-  describe("version()", () => {
35
-    it("Should return a supported version of the glance API.", (done) => {
36
-      const glance = new Glance(mockData.config);
37
-
38
-      fetchMock.mock(mockData.root());
39
-
40
-      glance.version()
41
-        .then((version) => {
42
-          expect(version.id).toEqual('v2.3');
43
-          done();
44
-        })
45
-        .catch((error) => done.fail(error));
46
-    });
47
-  });
48
-
49 34
   describe("serviceEndpoint()", () => {
50 35
     it("Should return a valid endpoint to the glance API.", (done) => {
51 36
       const glance = new Glance(mockData.config);

+ 0
- 10
test/unit/helpers/data/versions.js View File

@@ -75,16 +75,6 @@ function rootResponse() {
75 75
             }
76 76
           ]
77 77
         },
78
-        {
79
-          status: "SUPPORTED",
80
-          id: "v2.0",
81
-          links: [
82
-            {
83
-              href: `${rootUrl}v2/`,
84
-              rel: "self"
85
-            }
86
-          ]
87
-        },
88 78
         {
89 79
           status: "SUPPORTED",
90 80
           id: "v1.1",

+ 0
- 37
test/unit/keystoneTest.js View File

@@ -15,43 +15,6 @@ describe('Keystone', () => {
15 15
     expect(() => new Keystone()).toThrow();
16 16
   });
17 17
 
18
-  describe("versions()", () => {
19
-
20
-    /**
21
-     * Keystone needs an explicit test, as it uses a slightly different data format
22
-     * than other services.
23
-     */
24
-    it("Should return a list of all versions available on this clouds' keystone", (done) => {
25
-      const keystone = new Keystone(mockData.config);
26
-
27
-      fetchMock.mock(mockData.root());
28
-
29
-      keystone.versions()
30
-        .then((versions) => {
31
-          // Quick sanity check.
32
-          expect(versions.length).toBe(2);
33
-          done();
34
-        })
35
-        .catch((error) => done.fail(error));
36
-    });
37
-  });
38
-
39
-  describe("version()", () => {
40
-
41
-    it("Should return a supported version of the keystone API.", (done) => {
42
-      const keystone = new Keystone(mockData.config);
43
-
44
-      fetchMock.mock(mockData.root());
45
-
46
-      keystone.version()
47
-        .then((version) => {
48
-          expect(version.id).toEqual('v3.7');
49
-          done();
50
-        })
51
-        .catch((error) => done.fail(error));
52
-    });
53
-  });
54
-
55 18
   describe("serviceEndpoint()", () => {
56 19
     it("Should return a valid endpoint to the keystone API.", (done) => {
57 20
       const keystone = new Keystone(mockData.config);

+ 0
- 16
test/unit/neutronTest.js View File

@@ -36,22 +36,6 @@ describe('neutron', () => {
36 36
     }
37 37
   });
38 38
 
39
-  describe("versions()", () => {
40
-    it("Should return a list of all versions available on this clouds' NEUTRON", (done) => {
41
-      const neutron = new Neutron(mockData.config);
42
-
43
-      fetchMock.mock(mockData.root());
44
-
45
-      neutron.versions()
46
-        .then((versions) => {
47
-          // Quick sanity check.
48
-          expect(versions.length).toBe(1);
49
-          done();
50
-        })
51
-        .catch((error) => done.fail(error));
52
-    });
53
-  });
54
-
55 39
   describe("networkList()", () => {
56 40
     let neutron = null;
57 41
 

+ 23
- 17
test/unit/util/abstractServiceTest.js View File

@@ -53,21 +53,12 @@ describe('AbstractService', () => {
53 53
       service.versions()
54 54
         .then((versions) => {
55 55
           // Quick sanity check.
56
-          expect(versions.length).toBe(6);
57
-          done();
58
-        })
59
-        .catch((error) => done.fail(error));
60
-    });
61
-
62
-    it("Should return a list of all versions available from this resource", (done) => {
63
-      const service = new AbstractService(mockData.rootUrl, mockData.versions);
64
-
65
-      fetchMock.mock(mockData.rootResponse());
66
-
67
-      service.versions()
68
-        .then((versions) => {
69
-          // Quick sanity check.
70
-          expect(versions.length).toBe(6);
56
+          expect(versions.length).toBe(5);
57
+          expect(versions[0].major).toEqual(2);
58
+          expect(versions[0].minor).toEqual(3);
59
+          expect(versions[0].patch).toEqual(0);
60
+          expect(versions[0].links).not.toBe(null);
61
+          expect(versions[0].links[0].href).toEqual('http://example.com/v2/');
71 62
           done();
72 63
         })
73 64
         .catch((error) => done.fail(error));
@@ -84,7 +75,7 @@ describe('AbstractService', () => {
84 75
       service.versions()
85 76
         .then((versions) => {
86 77
           // Quick sanity check.
87
-          expect(versions.length).toBe(6);
78
+          expect(versions.length).toBe(5);
88 79
           done();
89 80
         })
90 81
         .catch((error) => done.fail(error));
@@ -132,7 +123,22 @@ describe('AbstractService', () => {
132 123
 
133 124
       service.version()
134 125
         .then((version) => {
135
-          expect(version.id).toEqual('v2.3');
126
+          expect(version.equals('v2.3')).toBe(true);
127
+          done();
128
+        })
129
+        .catch((error) => done.fail(error));
130
+    });
131
+
132
+    it("Should return the latest compatible version of the service API.", (done) => {
133
+      const service = new AbstractService(mockData.rootUrl, [
134
+        'v2.0'
135
+      ]);
136
+
137
+      fetchMock.mock(mockData.rootResponse());
138
+
139
+      service.version()
140
+        .then((version) => {
141
+          expect(version.equals('v2.3')).toBe(true);
136 142
           done();
137 143
         })
138 144
         .catch((error) => done.fail(error));

+ 59
- 0
test/unit/util/versionTest.js View File

@@ -96,4 +96,63 @@ describe('Version', () => {
96 96
     // Other tests...
97 97
     expect(v2.equals({})).toBe(false);
98 98
   });
99
+
100
+  it("should test for correct compatibility", () => {
101
+    const v1 = new Version("compute", "1.3.2");
102
+
103
+    // String tests
104
+    expect(v1.supports("compute 1.0.0")).toBe(true);
105
+    expect(v1.supports("compute 1.0.1")).toBe(true);
106
+    expect(v1.supports("compute 1.3.0")).toBe(true);
107
+    expect(v1.supports("compute 1.3.3")).toBe(false);
108
+    expect(v1.supports("compute 1.4.0")).toBe(false);
109
+    expect(v1.supports("compute 2.3.0")).toBe(false);
110
+
111
+    // Version tests
112
+    expect(v1.supports(new Version("compute", "1.0.0"))).toBe(true);
113
+    expect(v1.supports(new Version("compute", "1.0.1"))).toBe(true);
114
+    expect(v1.supports(new Version("compute", "1.3.0"))).toBe(true);
115
+    expect(v1.supports(new Version("compute", "1.3.3"))).toBe(false);
116
+    expect(v1.supports(new Version("compute", "1.4.0"))).toBe(false);
117
+    expect(v1.supports(new Version("compute", "2.3.0"))).toBe(false);
118
+
119
+    const v2 = new Version("1.3.2");
120
+    // String tests
121
+    expect(v2.supports("1.0.0")).toBe(true);
122
+    expect(v2.supports("1.0.1")).toBe(true);
123
+    expect(v2.supports("1.3.0")).toBe(true);
124
+    expect(v2.supports("1.3.3")).toBe(false);
125
+    expect(v2.supports("1.4.0")).toBe(false);
126
+    expect(v2.supports("2.3.0")).toBe(false);
127
+
128
+    // Version tests
129
+    expect(v2.supports(new Version("1.0.0"))).toBe(true);
130
+    expect(v2.supports(new Version("1.0.1"))).toBe(true);
131
+    expect(v2.supports(new Version("1.3.0"))).toBe(true);
132
+    expect(v2.supports(new Version("1.3.3"))).toBe(false);
133
+    expect(v2.supports(new Version("1.4.0"))).toBe(false);
134
+    expect(v2.supports(new Version("2.3.0"))).toBe(false);
135
+  });
136
+
137
+  it("should store links", () => {
138
+    const v1 = new Version("compute", "1.3.2");
139
+
140
+    expect(v1.links).toBe(null);
141
+
142
+    v1.links = 'wrong data';
143
+    expect(v1.links).toBe(null);
144
+
145
+    v1.links = [
146
+      {
147
+        href: `http://example.org/v2/`,
148
+        rel: "self"
149
+      }
150
+    ];
151
+    expect(v1.links).not.toBe(null);
152
+    expect(v1.links.length).toBe(1);
153
+    expect(v1.links[0]).toEqual({
154
+      href: "http://example.org/v2/",
155
+      rel: "self"
156
+    });
157
+  });
99 158
 });

Loading…
Cancel
Save