Skip to content

Commit 4cfd334

Browse files
authored
Merge pull request #62 from deathiop/v2
Allow /v1/ and /v2 in paths
2 parents d7b3235 + 3f38947 commit 4cfd334

File tree

5 files changed

+84
-14
lines changed

5 files changed

+84
-14
lines changed

README.md

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
go-ovh
22
======
33

4-
Lightweight Go wrapper around OVH's APIs. Handles all the hard work including credential creation and requests signing.
4+
Lightweight Go wrapper around OVHcloud's APIs. Handles all the hard work including credential creation and requests signing.
55

66
[![GoDoc](https://godoc.org/github.com/ovh/go-ovh/go-ovh?status.svg)](http://godoc.org/github.com/ovh/go-ovh/ovh)
77
[![Build Status](https://github.com/ovh/go-ovh/actions/workflows/golang-build.yaml/badge.svg?branch=master)](https://github.com/ovh/go-ovh/actions?query=workflow:golang-build)
@@ -53,7 +53,7 @@ import (
5353

5454
## Configuration
5555

56-
The straightforward way to use OVH's API keys is to embed them directly in the
56+
The straightforward way to use OVHcloud's API keys is to embed them directly in the
5757
application code. While this is very convenient, it lacks of elegance and
5858
flexibility.
5959

@@ -80,9 +80,9 @@ consumer_key=my_consumer_key
8080

8181
Depending on the API you want to use, you may set the ``endpoint`` to:
8282

83-
* ``ovh-eu`` for OVH Europe API
84-
* ``ovh-us`` for OVH US API
85-
* ``ovh-ca`` for OVH Canada API
83+
* ``ovh-eu`` for OVHcloud Europe API
84+
* ``ovh-us`` for OVHcloud US API
85+
* ``ovh-ca`` for OVHcloud Canada API
8686
* ``soyoustart-eu`` for So you Start Europe API
8787
* ``soyoustart-ca`` for So you Start Canada API
8888
* ``kimsufi-eu`` for Kimsufi Europe API
@@ -100,15 +100,15 @@ project or user.
100100

101101
## Register your app
102102

103-
OVH's API, like most modern APIs is designed to authenticate both an application and
103+
OVHcloud's API, like most modern APIs is designed to authenticate both an application and
104104
a user, without requiring the user to provide a password. Your application will be
105105
identified by its "application secret" and "application key" tokens.
106106

107107
Hence, to use the API, you must first register your application and then ask your
108108
user to authenticate on a specific URL. Once authenticated, you'll have a valid
109109
"consumer key" which will grant your application on specific APIs.
110110

111-
The user may choose the validity period of its authorization. The default period is
111+
The user may choose the validity period of his authorization. The default period is
112112
24h. He may also revoke an authorization at any time. Hence, your application should
113113
be prepared to receive 403 HTTP errors and prompt the user to re-authenticated.
114114

@@ -126,7 +126,6 @@ The consumer key has two types of restriction:
126126
* path: eg. only the ```GET``` method on ```/me```
127127
* time: eg. expire in 1 day
128128

129-
130129
Then, get a consumer key. Here's an example on how to generate one.
131130

132131
First, create a 'ovh.conf' file in the current directory with the application key and
@@ -282,6 +281,30 @@ func main() {
282281
}
283282
```
284283

284+
### Use v1 and v2 API versions
285+
286+
When using OVHcloud APIs (not So you Start or Kimsufi ones), you are given the
287+
opportunity to aim for two API versions. For the European API, for example:
288+
289+
- the v1 is reachable through https://eu.api.ovh.com/v1
290+
- the v2 is reachable through https://eu.api.ovh.com/v2
291+
- the legacy URL is https://eu.api.ovh.com/1.0
292+
293+
Calling `client.Get`, you can target the API version you want:
294+
295+
```go
296+
client, _ := ovh.NewEndpointClient("ovh-eu")
297+
298+
// Call to https://eu.api.ovh.com/v1/xdsl/xdsl-yourservice
299+
client.Get("/v1/xdsl/xdsl-yourservice", nil)
300+
301+
// Call to https://eu.api.ovh.com/v2/xdsl/xdsl-yourservice
302+
client.Get("/v2/xdsl/xdsl-yourservice", nil)
303+
304+
// Legacy call to https://eu.api.ovh.com/1.0/xdsl/xdsl-yourservice
305+
client.Get("/xdsl/xdsl-yourservice", nil)
306+
```
307+
285308
## API Documentation
286309

287310
### Create a client
@@ -310,7 +333,7 @@ Alternatively, you may directly use the low level ``CallAPI`` method.
310333
- Use ``client.Put()`` for PUT requests
311334
- Use ``client.Delete()`` for DELETE requests
312335

313-
Or, for unautenticated requests:
336+
Or, for unauthenticated requests:
314337

315338
- Use ``client.GetUnAuth()`` for GET requests
316339
- Use ``client.PostUnAuth()`` for POST requests
@@ -444,22 +467,22 @@ go vet ./...
444467

445468
## Supported APIs
446469

447-
### OVH Europe
470+
### OVHcloud Europe
448471

449472
- **Documentation**: https://eu.api.ovh.com/
450473
- **Community support**: [email protected]
451474
- **Console**: https://eu.api.ovh.com/console
452475
- **Create application credentials**: https://eu.api.ovh.com/createApp/
453476
- **Create script credentials** (all keys at once): https://eu.api.ovh.com/createToken/
454477

455-
### OVH US
478+
### OVHcloud US
456479

457480
- **Documentation**: https://api.us.ovhcloud.com/
458481
- **Console**: https://api.us.ovhcloud.com/console/
459482
- **Create application credentials**: https://api.us.ovhcloud.com/createApp/
460483
- **Create script credentials** (all keys at once): https://api.us.ovhcloud.com/createToken/
461484

462-
### OVH Canada
485+
### OVHcloud Canada
463486

464487
- **Documentation**: https://ca.api.ovh.com/
465488
- **Community support**: [email protected]

ovh/configuration.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ func loadINI() (*ini.File, error) {
8686
// - $HOME/.ovh.conf
8787
// - /etc/ovh.conf
8888
func (c *Client) loadConfig(endpointName string) error {
89+
if strings.HasSuffix(endpointName, "/") {
90+
return fmt.Errorf("endpoint name cannot have a tailing slash")
91+
}
92+
8993
// Load configuration files by order of increasing priority. All configuration
9094
// files are optional. Only load file from user home if home could be resolve
9195
cfg, err := loadINI()

ovh/configuration_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ func setConfigPaths(t testing.TB, paths ...string) {
2323
t.Cleanup(func() { configPaths = old })
2424
}
2525

26+
func TestConfigForbidsTrailingSlash(t *testing.T) {
27+
client := Client{}
28+
err := client.loadConfig("https://example.org/")
29+
td.Require(t).String(err, "endpoint name cannot have a tailing slash")
30+
}
31+
2632
func TestConfigFromFiles(t *testing.T) {
2733
setConfigPaths(t, systemConf, userPartialConf, localPartialConf)
2834

ovh/ovh.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"io/ioutil"
1212
"net/http"
1313
"strconv"
14+
"strings"
1415
"sync/atomic"
1516
"time"
1617
)
@@ -250,6 +251,17 @@ func (c *Client) getTime() (*time.Time, error) {
250251
return &serverTime, nil
251252
}
252253

254+
// getTarget returns the URL to target given and endpoint and a path.
255+
// If the path starts with `/v1` or `/v2`, then remove the trailing `/1.0` from the endpoint.
256+
func getTarget(endpoint, path string) string {
257+
// /1.0 + /v1/ or /1.0 + /v2/
258+
if strings.HasSuffix(endpoint, "/1.0") && (strings.HasPrefix(path, "/v1/") || strings.HasPrefix(path, "/v2/")) {
259+
return endpoint[:len(endpoint)-4] + path
260+
}
261+
262+
return endpoint + path
263+
}
264+
253265
// NewRequest returns a new HTTP request
254266
func (c *Client) NewRequest(method, path string, reqBody interface{}, needAuth bool) (*http.Request, error) {
255267
var body []byte
@@ -262,8 +274,7 @@ func (c *Client) NewRequest(method, path string, reqBody interface{}, needAuth b
262274
}
263275
}
264276

265-
target := fmt.Sprintf("%s%s", c.endpoint, path)
266-
req, err := http.NewRequest(method, target, bytes.NewReader(body))
277+
req, err := http.NewRequest(method, getTarget(c.endpoint, path), bytes.NewReader(body))
267278
if err != nil {
268279
return nil, err
269280
}

ovh/ovh_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,29 @@ func TestConstructors(t *testing.T) {
429429
require.CmpNoError(err)
430430
assert.Cmp(client, expected)
431431
}
432+
433+
func (ms *MockSuite) TestVersionInURL(assert, require *td.T) {
434+
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/1.0/call", httpmock.NewStringResponder(200, "{}"))
435+
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v1/call", httpmock.NewStringResponder(200, "{}"))
436+
httpmock.RegisterResponder("GET", "https://eu.api.ovh.com/v2/call", httpmock.NewStringResponder(200, "{}"))
437+
438+
assertCallCount := func(assert *td.T, ccNoVersion, ccV1, ccV2 int) {
439+
assert.Helper()
440+
assert.Cmp(httpmock.GetCallCountInfo(), map[string]int{
441+
"GET https://eu.api.ovh.com/1.0/call": ccNoVersion,
442+
"GET https://eu.api.ovh.com/v1/call": ccV1,
443+
"GET https://eu.api.ovh.com/v2/call": ccV2,
444+
})
445+
}
446+
447+
require.Cmp(ms.client.endpoint, "https://eu.api.ovh.com/1.0")
448+
449+
require.CmpNoError(ms.client.GetUnAuth("/call", nil))
450+
assertCallCount(assert, 1, 0, 0)
451+
452+
require.CmpNoError(ms.client.GetUnAuth("/v1/call", nil))
453+
assertCallCount(assert, 1, 1, 0)
454+
455+
require.CmpNoError(ms.client.GetUnAuth("/v2/call", nil))
456+
assertCallCount(assert, 1, 1, 1)
457+
}

0 commit comments

Comments
 (0)