Skip to content

Commit b1dc08a

Browse files
deathioprbeuque74
authored andcommitted
feat: allow /v1 or /v2 prefixes in path
Signed-off-by: Adrien Barreau <adrien.barreau@ovhcloud.com>
1 parent c99debc commit b1dc08a

File tree

3 files changed

+74
-6
lines changed

3 files changed

+74
-6
lines changed

README.rst

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ customer's information:
150150
print("Welcome", client.get('/me')['firstname'])
151151
print("Btw, your 'consumerKey' is '%s'" % validation['consumerKey'])
152152
153-
154153
Returned ``consumerKey`` should then be kept to avoid re-authenticating your
155154
end-user on each use.
156155

@@ -414,6 +413,31 @@ Example usage:
414413
415414
client = ovh.Client()
416415
416+
Use v1 and v2 API versions
417+
--------------------------
418+
419+
When using OVHcloud APIs (not So you Start or Kimsufi ones), you are given the
420+
opportunity to aim for two API versions. For the European API, for example:
421+
422+
- the v1 is reachable through https://eu.api.ovh.com/v1
423+
- the v2 is reachable through https://eu.api.ovh.com/v2
424+
- the legacy URL is https://eu.api.ovh.com/1.0
425+
426+
Calling ``client.get``, you can target the API version you want:
427+
428+
.. code:: python
429+
430+
client = ovh.Client(endpoint="ovh-eu")
431+
432+
# Call to https://eu.api.ovh.com/v1/xdsl/xdsl-yourservice
433+
client.get("/v1/xdsl/xdsl-yourservice")
434+
435+
# Call to https://eu.api.ovh.com/v2/xdsl/xdsl-yourservice
436+
client.get("/v2/xdsl/xdsl-yourservice")
437+
438+
# Legacy call to https://eu.api.ovh.com/1.0/xdsl/xdsl-yourservice
439+
client.get("/xdsl/xdsl-yourservice")
440+
417441
Custom configuration file
418442
-------------------------
419443

@@ -425,7 +449,6 @@ Example usage:
425449
426450
client = ovh.Client(config_file='/my/config.conf')
427451
428-
429452
Passing parameters
430453
==================
431454

@@ -495,8 +518,8 @@ Get the sources
495518
cd python-ovh
496519
python setup.py develop
497520
498-
You've developed a new cool feature ? Fixed an annoying bug ? We'd be happy
499-
to hear from you !
521+
You've developed a new cool feature? Fixed an annoying bug? We'd be happy
522+
to hear from you!
500523

501524
Run the tests
502525
-------------
@@ -510,7 +533,6 @@ note that we do not accept contributions with test coverage under 100%.
510533
pip install -e .[dev]
511534
pytest
512535
513-
514536
Build the documentation
515537
-----------------------
516538

ovh/client.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,20 @@ def call(self, method, path, data=None, need_auth=True):
472472
else:
473473
raise APIError(json_result.get("message"), response=result)
474474

475+
def _get_target(self, path):
476+
"""
477+
_get_target returns the URL to target given an endpoint and a path.
478+
If the path starts with `/v1` or `/v2`, then remove the trailing `/1.0` from the endpoint.
479+
480+
:param str path: path to use prefix from
481+
:returns: target with one of /1.0 and /v1|2 path segment
482+
:rtype: str
483+
"""
484+
endpoint = self._endpoint
485+
if endpoint.endswith("/1.0") and path.startswith(("/v1", "/v2")):
486+
endpoint = endpoint[:-4]
487+
return endpoint + path
488+
475489
def raw_call(self, method, path, data=None, need_auth=True, headers=None):
476490
"""
477491
Lowest level call helper. If ``consumer_key`` is not ``None``, inject
@@ -497,7 +511,7 @@ def raw_call(self, method, path, data=None, need_auth=True, headers=None):
497511
the Content-Type header.
498512
"""
499513
body = ""
500-
target = self._endpoint + path
514+
target = self._get_target(path)
501515

502516
if headers is None:
503517
headers = {}

tests/test_client.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,35 @@ def test_endpoints(self):
275275
for endpoint in ENDPOINTS.keys():
276276
auth_time = Client(endpoint).get("/auth/time", _need_auth=False)
277277
assert auth_time > 0
278+
279+
@mock.patch("time.time", return_value=1457018875.467238)
280+
@mock.patch("ovh.client.Session.request")
281+
@mock.patch("ovh.client.Client.time_delta", new_callable=mock.PropertyMock, return_value=0)
282+
def test_version_in_url(self, m_time_delta, m_req, m_time):
283+
m_res = m_req.return_value
284+
m_res.status_code = 200
285+
286+
api = Client("ovh-eu", MockApplicationKey, MockApplicationSecret, MockConsumerKey)
287+
api.call("GET", "/call", None, True)
288+
api.call("GET", "/v1/call", None, True)
289+
api.call("GET", "/v2/call", None, True)
290+
291+
signatures = {
292+
"1.0": "$1$7f2db49253edfc41891023fcd1a54cf61db05fbb",
293+
"v1": "$1$e6e7906d385eb28adcbfbe6b66c1528a42d741ad",
294+
"v2": "$1$bb63b132a6f84ad5433d0c534d48d3f7c3804285",
295+
}
296+
297+
def _h(prefix):
298+
return {
299+
"X-Ovh-Application": MockApplicationKey,
300+
"X-Ovh-Consumer": MockConsumerKey,
301+
"X-Ovh-Timestamp": str(MockTime),
302+
"X-Ovh-Signature": signatures[prefix],
303+
}
304+
305+
assert m_req.call_args_list == [
306+
mock.call("GET", "https://eu.api.ovh.com/1.0/call", headers=_h("1.0"), data="", timeout=180),
307+
mock.call("GET", "https://eu.api.ovh.com/v1/call", headers=_h("v1"), data="", timeout=180),
308+
mock.call("GET", "https://eu.api.ovh.com/v2/call", headers=_h("v2"), data="", timeout=180),
309+
]

0 commit comments

Comments
 (0)