Skip to content

Commit

Permalink
New: support for account/logins endpoints (#287)
Browse files Browse the repository at this point in the history
This adds support for the `/account/logins` and `account/logins/{id}` endpoints ([docs](https://www.linode.com/docs/api/account/#user-logins-list-all)).

Example usage:

```go
res, err := linodeClient.ListLogins(context.Background(), nil)
if err != nil {
	log.Fatal(err)
}

for _, l := range res {
	fmt.Println(l.IP)
}
```
  • Loading branch information
jcallahan-akamai authored Jan 17, 2023
1 parent f1725e9 commit d4d5e88
Show file tree
Hide file tree
Showing 3 changed files with 346 additions and 0 deletions.
79 changes: 79 additions & 0 deletions account_logins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package linodego

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/go-resty/resty/v2"
"github.com/linode/linodego/internal/parseabletime"
)

type Login struct {
ID int `json:"id"`
Datetime *time.Time `json:"datetime"`
IP string `json:"ip"`
Restricted bool `json:"restricted"`
Username string `json:"username"`
}

type LoginsPagedResponse struct {
*PageOptions
Data []Login `json:"data"`
}

func (LoginsPagedResponse) endpoint(_ ...any) string {
return "account/logins"
}

func (resp *LoginsPagedResponse) castResult(r *resty.Request, e string) (int, int, error) {
res, err := coupleAPIErrors(r.SetResult(LoginsPagedResponse{}).Get(e))
if err != nil {
return 0, 0, err
}
castedRes := res.Result().(*LoginsPagedResponse)
resp.Data = append(resp.Data, castedRes.Data...)
return castedRes.Pages, castedRes.Results, nil
}

func (c *Client) ListLogins(ctx context.Context, opts *ListOptions) ([]Login, error) {
response := LoginsPagedResponse{}
err := c.listHelper(ctx, &response, opts)
if err != nil {
return nil, err
}

return response.Data, nil
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (i *Login) UnmarshalJSON(b []byte) error {
type Mask Login

l := struct {
*Mask
Datetime *parseabletime.ParseableTime `json:"datetime"`
}{
Mask: (*Mask)(i),
}

if err := json.Unmarshal(b, &l); err != nil {
return err
}

i.Datetime = (*time.Time)(l.Datetime)

return nil
}

func (c *Client) GetLogin(ctx context.Context, loginID int) (*Login, error) {
req := c.R(ctx).SetResult(&Login{})
e := fmt.Sprintf("account/logins/%d", loginID)
r, err := coupleAPIErrors(req.Get(e))
if err != nil {
return nil, err
}

return r.Result().(*Login), nil
}
32 changes: 32 additions & 0 deletions test/integration/account_logins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package integration

import (
"context"
"testing"
)

func TestAccountLogins_List(t *testing.T) {
client, teardown := createTestClient(t, "fixtures/TestAccountLogins_List")
defer teardown()

logins, err := client.ListLogins(context.Background(), nil)

if err != nil {
t.Errorf("Error getting Account Logins, expected struct, got error %v", err)
}

if len(logins) < 1 {
t.Errorf("Expected to see at least one Account Login")
}

login := logins[0]

response, err := client.GetLogin(context.Background(), login.ID)
if err != nil {
t.Errorf("Failed to get one Account Login: %v", err)
}

if response.Username != login.Username {
t.Fatal("Recieved Account Login Username does not match source")
}
}
235 changes: 235 additions & 0 deletions test/integration/fixtures/TestAccountLogins_List.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
---
version: 1
interactions:
- request:
body: ""
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
User-Agent:
- linodego/dev https://github.com/linode/linodego
url: https://api.linode.com/v4beta/account/logins
method: GET
response:
body:
'{"data": [{"id": 1563764434, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678",
"username": "jcallahan-akamai", "restricted": false}, {"id": 1563766105, "datetime":
"2018-01-02T03:04:05", "ip": "1234::5678", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563766117, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563766137, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563766144, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563766178, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563766223, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563767269, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563767285, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563767367, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768248, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768690, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768714, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768738, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768843, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768877, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768895, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768903, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768932, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563768964, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769090, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769181, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769187, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769197, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769203, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769212, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769269, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769345, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769542, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769553, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769590, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769598, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769637, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769643, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769670, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769682, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563769733, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563780234, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563782760, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563783100, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563783225, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563861195, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563880170, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563880322, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563884474, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563884499, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563884546, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563898248, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678",
"username": "jcallahan-akamai", "restricted": false}, {"id": 1563947199, "datetime":
"2018-01-02T03:04:05", "ip": "172.104.2.4", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563948262, "datetime": "2018-01-02T03:04:05",
"ip": "172.104.2.4", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563961039, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563961386, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563966007, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678",
"username": "jcallahan-akamai", "restricted": false}, {"id": 1563977382, "datetime":
"2018-01-02T03:04:05", "ip": "172.104.2.4", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1563996063, "datetime": "2018-01-02T03:04:05",
"ip": "172.104.2.4", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563998765, "datetime": "2018-01-02T03:04:05", "ip": "172.104.2.4", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563998782, "datetime": "2018-01-02T03:04:05",
"ip": "172.104.2.4", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563998798, "datetime": "2018-01-02T03:04:05", "ip": "172.104.2.4", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563998931, "datetime": "2018-01-02T03:04:05",
"ip": "172.104.2.4", "username": "jcallahan-akamai", "restricted": false}, {"id":
1563998958, "datetime": "2018-01-02T03:04:05", "ip": "172.104.2.4", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1563998998, "datetime": "2018-01-02T03:04:05",
"ip": "172.104.2.4", "username": "jcallahan-akamai", "restricted": false}, {"id":
1564000367, "datetime": "2018-01-02T03:04:05", "ip": "172.104.2.4", "username":
"jcallahan-akamai", "restricted": false}, {"id": 1564003326, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai",
"restricted": false}, {"id": 1564037775, "datetime": "2018-01-02T03:04:05",
"ip": "1234::5678", "username": "jcallahan-akamai", "restricted": false}],
"page": 1, "pages": 1, "results": 64}'
headers:
Access-Control-Allow-Credentials:
- "true"
Access-Control-Allow-Headers:
- Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter
Access-Control-Allow-Methods:
- HEAD, GET, OPTIONS, POST, PUT, DELETE
Access-Control-Allow-Origin:
- "*"
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Cache-Control:
- private, max-age=0, s-maxage=0, no-cache, no-store
- private, max-age=60, s-maxage=60
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json
Server:
- nginx
Strict-Transport-Security:
- max-age=31536000
Vary:
- Authorization, X-Filter
- Authorization, X-Filter
X-Accepted-Oauth-Scopes:
- account:read_only
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
- DENY
X-Oauth-Scopes:
- "*"
X-Ratelimit-Limit:
- "800"
X-Xss-Protection:
- 1; mode=block
status: 200 OK
code: 200
duration: ""
- request:
body: ""
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
User-Agent:
- linodego/dev https://github.com/linode/linodego
url: https://api.linode.com/v4beta/account/logins/1563764434
method: GET
response:
body:
'{"id": 1563764434, "datetime": "2018-01-02T03:04:05", "ip": "1234::5678",
"username": "jcallahan-akamai", "restricted": false}'
headers:
Access-Control-Allow-Credentials:
- "true"
Access-Control-Allow-Headers:
- Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter
Access-Control-Allow-Methods:
- HEAD, GET, OPTIONS, POST, PUT, DELETE
Access-Control-Allow-Origin:
- "*"
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Cache-Control:
- private, max-age=0, s-maxage=0, no-cache, no-store
- private, max-age=60, s-maxage=60
Content-Length:
- "153"
Content-Security-Policy:
- default-src 'none'
Content-Type:
- application/json
Server:
- nginx
Strict-Transport-Security:
- max-age=31536000
Vary:
- Authorization, X-Filter
- Authorization, X-Filter
X-Accepted-Oauth-Scopes:
- account:read_only
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- DENY
- DENY
X-Oauth-Scopes:
- "*"
X-Ratelimit-Limit:
- "800"
X-Xss-Protection:
- 1; mode=block
status: 200 OK
code: 200
duration: ""

0 comments on commit d4d5e88

Please sign in to comment.