Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrated profile_phone_number to regions from resty to request helpers #547

8 changes: 8 additions & 0 deletions kernels.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,18 @@ func (c *Client) ListKernels(ctx context.Context, opts *ListOptions) ([]LinodeKe
// GetKernel gets the kernel with the provided ID. This endpoint is cached by default.
func (c *Client) GetKernel(ctx context.Context, kernelID string) (*LinodeKernel, error) {
e := formatAPIPath("linode/kernels/%s", kernelID)

if result := c.getCachedResponse(e); result != nil {
result := result.(LinodeKernel)
return &result, nil
}

response, err := doGETRequest[LinodeKernel](ctx, c, e)
if err != nil {
return nil, err
}

c.addCachedResponse(e, response, nil)

return response, nil
}
14 changes: 12 additions & 2 deletions paged_response_structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,18 @@ type ObjectStorageClustersPagedResponse legacyPagedResponse[ObjectStorageCluster
// Deprecated: PaymentsPagedResponse exists for historical compatibility and should not be used.
type PaymentsPagedResponse legacyPagedResponse[Payment]

// Deprecated: RegionsAvailabilityPagedResponse exists for historical compatibility and should not be used.
type RegionsAvailabilityPagedResponse legacyPagedResponse[RegionAvailability]
// Deprecated: RegionsPagedResponse exists for historical compatibility and should not be used.
type RegionsPagedResponse legacyPagedResponse[Region]

// Deprecated: SSHKeysPagedResponse exists for historical compatibility and should not be used.
type SSHKeysPagedResponse legacyPagedResponse[SSHKey]

// Deprecated: TokensPagedResponse exists for historical compatibility and should not be used.
type (
TokensPagedResponse legacyPagedResponse[Token]
// Deprecated: RegionsAvailabilityPagedResponse exists for historical compatibility and should not be used.
RegionsAvailabilityPagedResponse legacyPagedResponse[RegionAvailability]
)

// Deprecated: StackscriptsPagedResponse exists for historical compatibility and should not be used.
type StackscriptsPagedResponse legacyPagedResponse[Stackscript]
Expand Down
36 changes: 0 additions & 36 deletions pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package linodego
*/

import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -91,41 +90,6 @@ type PagedResponse interface {
castResult(*resty.Request, string) (int, int, error)
}

// listHelper abstracts fetching and pagination for GET endpoints that
// do not require any Ids (top level endpoints).
// When opts (or opts.Page) is nil, all pages will be fetched and
// returned in a single (endpoint-specific)PagedResponse
// opts.results and opts.pages will be updated from the API response
func (c *Client) listHelper(ctx context.Context, pager PagedResponse, opts *ListOptions, ids ...any) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

req := c.R(ctx)
if err := applyListOptionsToRequest(opts, req); err != nil {
return err
}

pages, results, err := pager.castResult(req, pager.endpoint(ids...))
if err != nil {
return err
}
if opts == nil {
opts = &ListOptions{PageOptions: &PageOptions{Page: 0}}
}
if opts.PageOptions == nil {
opts.PageOptions = &PageOptions{Page: 0}
}
if opts.Page == 0 {
for page := 2; page <= pages; page++ {
opts.Page = page
if err := c.listHelper(ctx, pager, opts, ids...); err != nil {
return err
}
}
}

opts.Results = results
opts.Pages = pages
return nil
}

// flattenQueryStruct flattens a structure into a Resty-compatible query param map.
// Fields are mapped using the `query` struct tag.
func flattenQueryStruct(val any) (map[string]string, error) {
Expand Down
19 changes: 5 additions & 14 deletions profile_phone_number.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package linodego

import (
"context"
"encoding/json"
)

// SendPhoneNumberVerificationCodeOptions fields are those accepted by SendPhoneNumberVerificationCode
Expand All @@ -18,31 +17,23 @@ type VerifyPhoneNumberOptions struct {

// SendPhoneNumberVerificationCode sends a one-time verification code via SMS message to the submitted phone number.
func (c *Client) SendPhoneNumberVerificationCode(ctx context.Context, opts SendPhoneNumberVerificationCodeOptions) error {
body, err := json.Marshal(opts)
if err != nil {
return err
}

e := "profile/phone-number"
_, err = coupleAPIErrors(c.R(ctx).SetBody(string(body)).Post(e))
_, err := doPOSTRequest[any](ctx, c, e, opts)

return err
}

// DeletePhoneNumber deletes the verified phone number for the User making this request.
func (c *Client) DeletePhoneNumber(ctx context.Context) error {
e := "profile/phone-number"
_, err := coupleAPIErrors(c.R(ctx).Delete(e))
err := doDELETERequest(ctx, c, e)
return err
}

// VerifyPhoneNumber verifies a phone number by confirming the one-time code received via SMS message after accessing the Phone Verification Code Send command.
func (c *Client) VerifyPhoneNumber(ctx context.Context, opts VerifyPhoneNumberOptions) error {
body, err := json.Marshal(opts)
if err != nil {
return err
}

e := "profile/phone-number/verify"
_, err = coupleAPIErrors(c.R(ctx).SetBody(string(body)).Post(e))
_, err := doPOSTRequest[any](ctx, c, e, opts)

return err
}
16 changes: 5 additions & 11 deletions profile_security_questions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package linodego

import (
"context"
"encoding/json"
)

type SecurityQuestion struct {
Expand All @@ -27,23 +26,18 @@ type SecurityQuestionsAnswerOptions struct {
// SecurityQuestionsList returns a collection of security questions and their responses, if any, for your User Profile.
func (c *Client) SecurityQuestionsList(ctx context.Context) (*SecurityQuestionsListResponse, error) {
e := "profile/security-questions"
req := c.R(ctx).SetResult(&SecurityQuestionsListResponse{})
r, err := coupleAPIErrors(req.Get(e))
response, err := doGETRequest[SecurityQuestionsListResponse](ctx, c, e)
if err != nil {
return nil, err
}
return r.Result().(*SecurityQuestionsListResponse), nil

return response, nil
}

// SecurityQuestionsAnswer adds security question responses for your User.
func (c *Client) SecurityQuestionsAnswer(ctx context.Context, opts SecurityQuestionsAnswerOptions) error {
body, err := json.Marshal(opts)
if err != nil {
return err
}

e := "profile/security-questions"
req := c.R(ctx).SetBody(string(body))
_, err = coupleAPIErrors(req.Post(e))

_, err := doPOSTRequest[any](ctx, c, e, opts)
return err
}
63 changes: 15 additions & 48 deletions profile_sshkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ package linodego
import (
"context"
"encoding/json"
"fmt"
"time"

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

Expand Down Expand Up @@ -62,83 +60,52 @@ func (i SSHKey) GetUpdateOptions() (o SSHKeyUpdateOptions) {
return
}

// SSHKeysPagedResponse represents a paginated SSHKey API response
type SSHKeysPagedResponse struct {
*PageOptions
Data []SSHKey `json:"data"`
}

// endpoint gets the endpoint URL for SSHKey
func (SSHKeysPagedResponse) endpoint(_ ...any) string {
return "profile/sshkeys"
}

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

// ListSSHKeys lists SSHKeys
func (c *Client) ListSSHKeys(ctx context.Context, opts *ListOptions) ([]SSHKey, error) {
response := SSHKeysPagedResponse{}
err := c.listHelper(ctx, &response, opts)
response, err := getPaginatedResults[SSHKey](ctx, c, "profile/sshkeys", opts)
if err != nil {
return nil, err
}
return response.Data, nil

return response, nil
}

// GetSSHKey gets the sshkey with the provided ID
func (c *Client) GetSSHKey(ctx context.Context, keyID int) (*SSHKey, error) {
e := fmt.Sprintf("profile/sshkeys/%d", keyID)
req := c.R(ctx).SetResult(&SSHKey{})
r, err := coupleAPIErrors(req.Get(e))
e := formatAPIPath("profile/sshkeys/%d", keyID)
response, err := doGETRequest[SSHKey](ctx, c, e)
if err != nil {
return nil, err
}
return r.Result().(*SSHKey), nil

return response, nil
}

// CreateSSHKey creates a SSHKey
func (c *Client) CreateSSHKey(ctx context.Context, opts SSHKeyCreateOptions) (*SSHKey, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

e := "profile/sshkeys"
req := c.R(ctx).SetResult(&SSHKey{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Post(e))
response, err := doPOSTRequest[SSHKey](ctx, c, e, opts)
if err != nil {
return nil, err
}
return r.Result().(*SSHKey), nil

return response, nil
}

// UpdateSSHKey updates the SSHKey with the specified id
func (c *Client) UpdateSSHKey(ctx context.Context, keyID int, opts SSHKeyUpdateOptions) (*SSHKey, error) {
body, err := json.Marshal(opts)
e := formatAPIPath("profile/sshkeys/%d", keyID)
response, err := doPUTRequest[SSHKey](ctx, c, e, opts)
if err != nil {
return nil, err
}

e := fmt.Sprintf("profile/sshkeys/%d", keyID)
req := c.R(ctx).SetResult(&SSHKey{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Put(e))
if err != nil {
return nil, err
}
return r.Result().(*SSHKey), nil
return response, nil
}

// DeleteSSHKey deletes the SSHKey with the specified id
func (c *Client) DeleteSSHKey(ctx context.Context, keyID int) error {
e := fmt.Sprintf("profile/sshkeys/%d", keyID)
_, err := coupleAPIErrors(c.R(ctx).Delete(e))
e := formatAPIPath("profile/sshkeys/%d", keyID)
err := doDELETERequest(ctx, c, e)
return err
}
17 changes: 5 additions & 12 deletions profile_tfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,35 +46,28 @@ func (s *TwoFactorSecret) UnmarshalJSON(b []byte) error {
// CreateTwoFactorSecret generates a Two Factor secret for your User.
func (c *Client) CreateTwoFactorSecret(ctx context.Context) (*TwoFactorSecret, error) {
e := "profile/tfa-enable"
req := c.R(ctx).SetResult(&TwoFactorSecret{})
r, err := coupleAPIErrors(req.Post(e))
response, err := doPOSTRequest[TwoFactorSecret, any](ctx, c, e)
if err != nil {
return nil, err
}

return r.Result().(*TwoFactorSecret), nil
return response, nil
}

// DisableTwoFactor disables Two Factor Authentication for your User.
func (c *Client) DisableTwoFactor(ctx context.Context) error {
e := "profile/tfa-disable"
_, err := coupleAPIErrors(c.R(ctx).Post(e))
_, err := doPOSTRequest[TwoFactorSecret, any](ctx, c, e)
return err
}

// ConfirmTwoFactor confirms that you can successfully generate Two Factor codes and enables TFA on your Account.
func (c *Client) ConfirmTwoFactor(ctx context.Context, opts ConfirmTwoFactorOptions) (*ConfirmTwoFactorResponse, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

e := "profile/tfa-enable-confirm"
req := c.R(ctx).SetResult(&ConfirmTwoFactorResponse{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Post(e))
response, err := doPOSTRequest[ConfirmTwoFactorResponse](ctx, c, e, opts)
if err != nil {
return nil, err
}

return r.Result().(*ConfirmTwoFactorResponse), nil
return response, nil
}
Loading