From c884de889886a639480b674de86d0a349df4aeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Mon, 12 Jan 2026 22:48:16 +0100 Subject: [PATCH 01/10] Add openapi schema --- schema/client.go | 23 +++ schema/schema.gen.go | 455 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 478 insertions(+) create mode 100644 schema/client.go create mode 100644 schema/schema.gen.go diff --git a/schema/client.go b/schema/client.go new file mode 100644 index 0000000..1d9c240 --- /dev/null +++ b/schema/client.go @@ -0,0 +1,23 @@ +package schema + +import ( + "context" + "net/http" +) + +const ( + BaseUrl = "https://gateway.internxt.com/drive" + InternxtClient = "rclone" + InternxtVersion = "1.0" +) + +func NewInternxtClient(token string) (*Client, error) { + return NewClient(BaseUrl, + WithRequestEditorFn(func(ctx context.Context, req *http.Request) error { + req.Header.Set("internxt-client", InternxtClient) + req.Header.Set("internxt-version", InternxtVersion) + req.Header.Set("Authorization", "Bearer "+token) + return nil + }), + ) +} diff --git a/schema/schema.gen.go b/schema/schema.gen.go new file mode 100644 index 0000000..52d9269 --- /dev/null +++ b/schema/schema.gen.go @@ -0,0 +1,455 @@ +// Package schema provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT. +package schema + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + BearerScopes = "bearer.Scopes" +) + +// Defines values for FileDtoStatus. +const ( + DELETED FileDtoStatus = "DELETED" + EXISTS FileDtoStatus = "EXISTS" + TRASHED FileDtoStatus = "TRASHED" +) + +// CreateFileDto defines model for CreateFileDto. +type CreateFileDto struct { + // Bucket The bucket where the file is stored + Bucket string `json:"bucket"` + + // CreationTime The creation time of the file (optional) + CreationTime *time.Time `json:"creationTime,omitempty"` + + // Date The date associated with the file (optional) + Date *time.Time `json:"date,omitempty"` + + // EncryptVersion The encryption version used for the file + EncryptVersion string `json:"encryptVersion"` + + // FileId The ID of the file (required when size > 0, must not be provided when size = 0) + FileId *string `json:"fileId,omitempty"` + + // FolderUuid The UUID of the folder containing the file + FolderUuid openapi_types.UUID `json:"folderUuid"` + + // ModificationTime The last modification time of the file (optional) + ModificationTime *time.Time `json:"modificationTime,omitempty"` + + // PlainName The plain text name of the file + PlainName string `json:"plainName"` + + // Size The size of the file in bytes + Size float32 `json:"size"` + + // Type The type of the file (optional) + Type *string `json:"type,omitempty"` +} + +// FileDto defines model for FileDto. +type FileDto struct { + Bucket string `json:"bucket"` + CreatedAt time.Time `json:"createdAt"` + CreationTime time.Time `json:"creationTime"` + EncryptVersion string `json:"encryptVersion"` + FileId *string `json:"fileId"` + FolderId float32 `json:"folderId"` + FolderUuid string `json:"folderUuid"` + Id float32 `json:"id"` + ModificationTime time.Time `json:"modificationTime"` + Name string `json:"name"` + PlainName string `json:"plainName"` + Size string `json:"size"` + Status FileDtoStatus `json:"status"` + Type string `json:"type"` + UpdatedAt time.Time `json:"updatedAt"` + UserId float32 `json:"userId"` + Uuid string `json:"uuid"` +} + +// FileDtoStatus defines model for FileDto.Status. +type FileDtoStatus string + +// GetUserUsageDto defines model for GetUserUsageDto. +type GetUserUsageDto struct { + Backup float32 `json:"backup"` + Drive float32 `json:"drive"` + Total float32 `json:"total"` +} + +// FileControllerCreateFileJSONRequestBody defines body for FileControllerCreateFile for application/json ContentType. +type FileControllerCreateFileJSONRequestBody = CreateFileDto + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // FileControllerCreateFileWithBody request with any body + FileControllerCreateFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + FileControllerCreateFile(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UserControllerGetUserUsage request + UserControllerGetUserUsage(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) FileControllerCreateFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFileControllerCreateFileRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) FileControllerCreateFile(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFileControllerCreateFileRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UserControllerGetUserUsage(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUserControllerGetUserUsageRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewFileControllerCreateFileRequest calls the generic FileControllerCreateFile builder with application/json body +func NewFileControllerCreateFileRequest(server string, body FileControllerCreateFileJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewFileControllerCreateFileRequestWithBody(server, "application/json", bodyReader) +} + +// NewFileControllerCreateFileRequestWithBody generates requests for FileControllerCreateFile with any type of body +func NewFileControllerCreateFileRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/files") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewUserControllerGetUserUsageRequest generates requests for UserControllerGetUserUsage +func NewUserControllerGetUserUsageRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/users/usage") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // FileControllerCreateFileWithBodyWithResponse request with any body + FileControllerCreateFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*FileControllerCreateFileResponse, error) + + FileControllerCreateFileWithResponse(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*FileControllerCreateFileResponse, error) + + // UserControllerGetUserUsageWithResponse request + UserControllerGetUserUsageWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerGetUserUsageResponse, error) +} + +type FileControllerCreateFileResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *FileDto +} + +// Status returns HTTPResponse.Status +func (r FileControllerCreateFileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FileControllerCreateFileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type UserControllerGetUserUsageResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetUserUsageDto +} + +// Status returns HTTPResponse.Status +func (r UserControllerGetUserUsageResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UserControllerGetUserUsageResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// FileControllerCreateFileWithBodyWithResponse request with arbitrary body returning *FileControllerCreateFileResponse +func (c *ClientWithResponses) FileControllerCreateFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*FileControllerCreateFileResponse, error) { + rsp, err := c.FileControllerCreateFileWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseFileControllerCreateFileResponse(rsp) +} + +func (c *ClientWithResponses) FileControllerCreateFileWithResponse(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*FileControllerCreateFileResponse, error) { + rsp, err := c.FileControllerCreateFile(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseFileControllerCreateFileResponse(rsp) +} + +// UserControllerGetUserUsageWithResponse request returning *UserControllerGetUserUsageResponse +func (c *ClientWithResponses) UserControllerGetUserUsageWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerGetUserUsageResponse, error) { + rsp, err := c.UserControllerGetUserUsage(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseUserControllerGetUserUsageResponse(rsp) +} + +// ParseFileControllerCreateFileResponse parses an HTTP response from a FileControllerCreateFileWithResponse call +func ParseFileControllerCreateFileResponse(rsp *http.Response) (*FileControllerCreateFileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FileControllerCreateFileResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest FileDto + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseUserControllerGetUserUsageResponse parses an HTTP response from a UserControllerGetUserUsageWithResponse call +func ParseUserControllerGetUserUsageResponse(rsp *http.Response) (*UserControllerGetUserUsageResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UserControllerGetUserUsageResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetUserUsageDto + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} From d2ac3420342810345de4455f048fc156b45c1aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Mon, 12 Jan 2026 22:48:59 +0100 Subject: [PATCH 02/10] Update usage.go --- users/usage.go | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/users/usage.go b/users/usage.go index 09ef4c3..5a4abb5 100644 --- a/users/usage.go +++ b/users/usage.go @@ -2,42 +2,26 @@ package users import ( "context" - "encoding/json" "fmt" - "io" - "net/http" - "github.com/internxt/rclone-adapter/config" + "github.com/internxt/rclone-adapter/schema" ) -type UsageResponse struct { - Drive int64 `json:"drive"` -} - // GetUsage calls GET {DRIVE_API_URL}/users/usage and returns the account's current usage in bytes. -func GetUsage(ctx context.Context, cfg *config.Config) (*UsageResponse, error) { - url := cfg.Endpoints.Drive().Users().Usage() - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) +func GetUsage(ctx context.Context, client *schema.Client) (*schema.GetUserUsageDto, error) { + resp, err := client.UserControllerGetUserUsage(ctx) if err != nil { - return nil, fmt.Errorf("failed to create get usage request: %w", err) + return nil, fmt.Errorf("failed to get usage: %w", err) } - req.Header.Set("Authorization", "Bearer "+cfg.Token) - resp, err := cfg.HTTPClient.Do(req) + parsed, err := schema.ParseUserControllerGetUserUsageResponse(resp) if err != nil { - return nil, fmt.Errorf("failed to execute get usage request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("GET %s returned %d: %s", url, resp.StatusCode, string(body)) + return nil, fmt.Errorf("failed to parse get usage response: %w", err) } - var usage UsageResponse - if err := json.NewDecoder(resp.Body).Decode(&usage); err != nil { - return nil, fmt.Errorf("failed to decode get usage response: %w", err) + if parsed.JSON200 != nil { + return parsed.JSON200, nil } - return &usage, nil + return nil, fmt.Errorf("unexpected get usage response status: %d", resp.StatusCode) } From f43b0be12c01047ffb4aece363589e2b0c260cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Mon, 12 Jan 2026 23:01:51 +0100 Subject: [PATCH 03/10] Commit --- schema/schema.gen.go | 107 +++++++++++++++++++++++++++++++++++++++++++ users/limit.go | 36 ++++----------- users/usage.go | 4 +- 3 files changed, 119 insertions(+), 28 deletions(-) diff --git a/schema/schema.gen.go b/schema/schema.gen.go index 52d9269..1a7ba65 100644 --- a/schema/schema.gen.go +++ b/schema/schema.gen.go @@ -85,6 +85,11 @@ type FileDto struct { // FileDtoStatus defines model for FileDto.Status. type FileDtoStatus string +// GetUserLimitDto defines model for GetUserLimitDto. +type GetUserLimitDto struct { + MaxSpaceBytes float32 `json:"maxSpaceBytes"` +} + // GetUserUsageDto defines model for GetUserUsageDto. type GetUserUsageDto struct { Backup float32 `json:"backup"` @@ -173,6 +178,9 @@ type ClientInterface interface { FileControllerCreateFile(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // UserControllerLimit request + UserControllerLimit(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // UserControllerGetUserUsage request UserControllerGetUserUsage(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) } @@ -201,6 +209,18 @@ func (c *Client) FileControllerCreateFile(ctx context.Context, body FileControll return c.Client.Do(req) } +func (c *Client) UserControllerLimit(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUserControllerLimitRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) UserControllerGetUserUsage(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUserControllerGetUserUsageRequest(c.Server) if err != nil { @@ -253,6 +273,33 @@ func NewFileControllerCreateFileRequestWithBody(server string, contentType strin return req, nil } +// NewUserControllerLimitRequest generates requests for UserControllerLimit +func NewUserControllerLimitRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/users/limit") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewUserControllerGetUserUsageRequest generates requests for UserControllerGetUserUsage func NewUserControllerGetUserUsageRequest(server string) (*http.Request, error) { var err error @@ -328,6 +375,9 @@ type ClientWithResponsesInterface interface { FileControllerCreateFileWithResponse(ctx context.Context, body FileControllerCreateFileJSONRequestBody, reqEditors ...RequestEditorFn) (*FileControllerCreateFileResponse, error) + // UserControllerLimitWithResponse request + UserControllerLimitWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerLimitResponse, error) + // UserControllerGetUserUsageWithResponse request UserControllerGetUserUsageWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerGetUserUsageResponse, error) } @@ -354,6 +404,28 @@ func (r FileControllerCreateFileResponse) StatusCode() int { return 0 } +type UserControllerLimitResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetUserLimitDto +} + +// Status returns HTTPResponse.Status +func (r UserControllerLimitResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UserControllerLimitResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type UserControllerGetUserUsageResponse struct { Body []byte HTTPResponse *http.Response @@ -393,6 +465,15 @@ func (c *ClientWithResponses) FileControllerCreateFileWithResponse(ctx context.C return ParseFileControllerCreateFileResponse(rsp) } +// UserControllerLimitWithResponse request returning *UserControllerLimitResponse +func (c *ClientWithResponses) UserControllerLimitWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerLimitResponse, error) { + rsp, err := c.UserControllerLimit(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseUserControllerLimitResponse(rsp) +} + // UserControllerGetUserUsageWithResponse request returning *UserControllerGetUserUsageResponse func (c *ClientWithResponses) UserControllerGetUserUsageWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserControllerGetUserUsageResponse, error) { rsp, err := c.UserControllerGetUserUsage(ctx, reqEditors...) @@ -428,6 +509,32 @@ func ParseFileControllerCreateFileResponse(rsp *http.Response) (*FileControllerC return response, nil } +// ParseUserControllerLimitResponse parses an HTTP response from a UserControllerLimitWithResponse call +func ParseUserControllerLimitResponse(rsp *http.Response) (*UserControllerLimitResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UserControllerLimitResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetUserLimitDto + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + // ParseUserControllerGetUserUsageResponse parses an HTTP response from a UserControllerGetUserUsageWithResponse call func ParseUserControllerGetUserUsageResponse(rsp *http.Response) (*UserControllerGetUserUsageResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/users/limit.go b/users/limit.go index 3b08246..6193f96 100644 --- a/users/limit.go +++ b/users/limit.go @@ -2,42 +2,26 @@ package users import ( "context" - "encoding/json" "fmt" - "io" - "net/http" - "github.com/internxt/rclone-adapter/config" + "github.com/internxt/rclone-adapter/schema" ) -type LimitResponse struct { - MaxSpaceBytes int64 `json:"maxSpaceBytes"` -} - -// GetLimit calls {DRIVE_API_URL}/users/limit and returns the maximum available storage of the account. -func GetLimit(ctx context.Context, cfg *config.Config) (*LimitResponse, error) { - url := cfg.Endpoints.Drive().Users().Limit() - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) +// GetLimit calls GET {DRIVE_API_URL}/users/limit and returns the maximum available storage of the account. +func GetLimit(ctx context.Context, client *schema.Client) (*schema.GetUserLimitDto, error) { + resp, err := client.UserControllerLimit(ctx) if err != nil { - return nil, fmt.Errorf("failed to create get limit request: %w", err) + return nil, fmt.Errorf("failed to get limit: %w", err) } - req.Header.Set("Authorization", "Bearer "+cfg.Token) - resp, err := cfg.HTTPClient.Do(req) + parsed, err := schema.ParseUserControllerLimitResponse(resp) if err != nil { - return nil, fmt.Errorf("failed to execute get limit request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("GET %s returned %d: %s", url, resp.StatusCode, string(body)) + return nil, fmt.Errorf("failed to parse response: %w", err) } - var limit LimitResponse - if err := json.NewDecoder(resp.Body).Decode(&limit); err != nil { - return nil, fmt.Errorf("failed to decode get limit response: %w", err) + if parsed.JSON200 != nil { + return parsed.JSON200, nil } - return &limit, nil + return nil, fmt.Errorf("unexpected response status: %d", resp.StatusCode) } diff --git a/users/usage.go b/users/usage.go index 5a4abb5..1597c5f 100644 --- a/users/usage.go +++ b/users/usage.go @@ -16,12 +16,12 @@ func GetUsage(ctx context.Context, client *schema.Client) (*schema.GetUserUsageD parsed, err := schema.ParseUserControllerGetUserUsageResponse(resp) if err != nil { - return nil, fmt.Errorf("failed to parse get usage response: %w", err) + return nil, fmt.Errorf("failed to parse response: %w", err) } if parsed.JSON200 != nil { return parsed.JSON200, nil } - return nil, fmt.Errorf("unexpected get usage response status: %d", resp.StatusCode) + return nil, fmt.Errorf("unexpected response status: %d", resp.StatusCode) } From c421b34b3ae459dfe0d6ab79e925b97e91757ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Mon, 12 Jan 2026 23:04:38 +0100 Subject: [PATCH 04/10] Update users_test.go --- users/users_test.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/users/users_test.go b/users/users_test.go index a9204e0..b688bc3 100644 --- a/users/users_test.go +++ b/users/users_test.go @@ -7,19 +7,21 @@ import ( "net/http/httptest" "strings" "testing" + + "github.com/internxt/rclone-adapter/schema" ) func TestGetUsage(t *testing.T) { testCases := []struct { name string - mockResponse UsageResponse + mockResponse schema.GetUserUsageDto mockStatusCode int expectError bool errorContains string }{ { name: "successful usage retrieval", - mockResponse: UsageResponse{ + mockResponse: schema.GetUserUsageDto{ Drive: 1024 * 1024 * 1024, // 1 GB }, mockStatusCode: http.StatusOK, @@ -27,7 +29,7 @@ func TestGetUsage(t *testing.T) { }, { name: "zero usage", - mockResponse: UsageResponse{ + mockResponse: schema.GetUserUsageDto{ Drive: 0, }, mockStatusCode: http.StatusOK, @@ -125,14 +127,14 @@ func TestGetUsageInvalidJSON(t *testing.T) { func TestGetLimit(t *testing.T) { testCases := []struct { name string - mockResponse LimitResponse + mockResponse schema.GetUserLimitDto mockStatusCode int expectError bool errorContains string }{ { name: "successful limit retrieval", - mockResponse: LimitResponse{ + mockResponse: schema.GetUserLimitDto{ MaxSpaceBytes: 10 * 1024 * 1024 * 1024, // 10 GB }, mockStatusCode: http.StatusOK, @@ -140,7 +142,7 @@ func TestGetLimit(t *testing.T) { }, { name: "zero limit", - mockResponse: LimitResponse{ + mockResponse: schema.GetUserLimitDto{ MaxSpaceBytes: 0, }, mockStatusCode: http.StatusOK, @@ -162,7 +164,7 @@ func TestGetLimit(t *testing.T) { name: "not found - 404", mockStatusCode: http.StatusNotFound, expectError: true, - errorContains: "404", + errorContains: "404", }, } From c1e36a4ed3ddc0d97514b18ad8254231dc7c550e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Tue, 13 Jan 2026 09:53:51 +0100 Subject: [PATCH 05/10] Commit --- go.mod | 13 +++- go.sum | 14 ++++ schema/client.go | 13 ++-- users/users_test.go | 161 +++++++++++++------------------------------- 4 files changed, 75 insertions(+), 126 deletions(-) diff --git a/go.mod b/go.mod index 97b2cc6..bd5304f 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,19 @@ go 1.24.0 require ( github.com/disintegration/imaging v1.6.2 + github.com/oapi-codegen/runtime v1.1.2 github.com/tyler-smith/go-bip39 v1.1.0 golang.org/x/crypto v0.45.0 ) -require golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +require ( + github.com/stretchr/testify v1.11.1 + golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect +) diff --git a/go.sum b/go.sum index 4e9c265..9960a38 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,15 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -12,3 +22,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/schema/client.go b/schema/client.go index 1d9c240..a516761 100644 --- a/schema/client.go +++ b/schema/client.go @@ -3,19 +3,14 @@ package schema import ( "context" "net/http" -) -const ( - BaseUrl = "https://gateway.internxt.com/drive" - InternxtClient = "rclone" - InternxtVersion = "1.0" + "github.com/internxt/rclone-adapter/config" ) -func NewInternxtClient(token string) (*Client, error) { - return NewClient(BaseUrl, +func NewInternxtClient(baseUrl, token string) (*Client, error) { + return NewClient(baseUrl, WithRequestEditorFn(func(ctx context.Context, req *http.Request) error { - req.Header.Set("internxt-client", InternxtClient) - req.Header.Set("internxt-version", InternxtVersion) + req.Header.Set("internxt-client", config.ClientName) req.Header.Set("Authorization", "Bearer "+token) return nil }), diff --git a/users/users_test.go b/users/users_test.go index b688bc3..3af9838 100644 --- a/users/users_test.go +++ b/users/users_test.go @@ -5,10 +5,10 @@ import ( "encoding/json" "net/http" "net/http/httptest" - "strings" "testing" "github.com/internxt/rclone-adapter/schema" + "github.com/stretchr/testify/assert" ) func TestGetUsage(t *testing.T) { @@ -16,7 +16,6 @@ func TestGetUsage(t *testing.T) { name string mockResponse schema.GetUserUsageDto mockStatusCode int - expectError bool errorContains string }{ { @@ -25,7 +24,6 @@ func TestGetUsage(t *testing.T) { Drive: 1024 * 1024 * 1024, // 1 GB }, mockStatusCode: http.StatusOK, - expectError: false, }, { name: "zero usage", @@ -33,74 +31,52 @@ func TestGetUsage(t *testing.T) { Drive: 0, }, mockStatusCode: http.StatusOK, - expectError: false, }, { name: "unauthorized - 401", mockStatusCode: http.StatusUnauthorized, - expectError: true, errorContains: "401", }, { name: "server error - 500", mockStatusCode: http.StatusInternalServerError, - expectError: true, errorContains: "500", }, { name: "forbidden - 403", mockStatusCode: http.StatusForbidden, - expectError: true, errorContains: "403", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - t.Errorf("expected GET request, got %s", r.Method) - } - authHeader := r.Header.Get("Authorization") - if !strings.HasPrefix(authHeader, "Bearer ") { - t.Error("expected Authorization header with Bearer token") - } - - if !strings.Contains(r.URL.Path, "/usage") { - t.Errorf("expected path to contain /usage, got %s", r.URL.Path) - } + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.Header.Get("Authorization"), "Bearer token") + assert.Contains(t, r.URL.Path, "/usage") - w.WriteHeader(tc.mockStatusCode) if tc.mockStatusCode == http.StatusOK { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(tc.mockStatusCode) json.NewEncoder(w).Encode(tc.mockResponse) } else { + w.WriteHeader(tc.mockStatusCode) w.Write([]byte("error message")) } })) defer mockServer.Close() - cfg := newTestConfig(mockServer.URL) - - usage, err := GetUsage(context.Background(), cfg) + client, _ := schema.NewInternxtClient(mockServer.URL, "token") + usage, err := GetUsage(context.Background(), client) - if tc.expectError { - if err == nil { - t.Error("expected error, got nil") - } - if tc.errorContains != "" && !strings.Contains(err.Error(), tc.errorContains) { - t.Errorf("expected error to contain %q, got %q", tc.errorContains, err.Error()) - } + if tc.errorContains != "" { + assert.ErrorContains(t, err, tc.errorContains) } else { - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if usage == nil { - t.Fatal("expected usage response, got nil") - } - if usage.Drive != tc.mockResponse.Drive { - t.Errorf("expected Drive %d, got %d", tc.mockResponse.Drive, usage.Drive) - } + assert.NoError(t, err) + assert.NotNil(t, usage) + assert.Equal(t, tc.mockResponse.Drive, usage.Drive) } }) } @@ -108,20 +84,22 @@ func TestGetUsage(t *testing.T) { func TestGetUsageInvalidJSON(t *testing.T) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte("invalid json response")) })) defer mockServer.Close() - cfg := newTestConfig(mockServer.URL) + client, _ := schema.NewInternxtClient(mockServer.URL, "token") + _, err := GetUsage(context.Background(), client) - _, err := GetUsage(context.Background(), cfg) - if err == nil { - t.Fatal("expected error for invalid JSON, got nil") - } - if !strings.Contains(err.Error(), "failed to decode") { - t.Errorf("expected error to contain 'failed to decode', got %q", err.Error()) - } + assert.ErrorContains(t, err, "failed to parse response") +} + +func TestGetUsageHTTPClientError(t *testing.T) { + client, _ := schema.NewInternxtClient("http://invalid-host-that-does-not-exist", "token") + _, err := GetUsage(context.Background(), client) + assert.ErrorContains(t, err, "no such host") } func TestGetLimit(t *testing.T) { @@ -129,7 +107,6 @@ func TestGetLimit(t *testing.T) { name string mockResponse schema.GetUserLimitDto mockStatusCode int - expectError bool errorContains string }{ { @@ -138,7 +115,6 @@ func TestGetLimit(t *testing.T) { MaxSpaceBytes: 10 * 1024 * 1024 * 1024, // 10 GB }, mockStatusCode: http.StatusOK, - expectError: false, }, { name: "zero limit", @@ -146,24 +122,20 @@ func TestGetLimit(t *testing.T) { MaxSpaceBytes: 0, }, mockStatusCode: http.StatusOK, - expectError: false, }, { name: "unauthorized - 401", mockStatusCode: http.StatusUnauthorized, - expectError: true, errorContains: "401", }, { name: "server error - 500", mockStatusCode: http.StatusInternalServerError, - expectError: true, errorContains: "500", }, { name: "not found - 404", mockStatusCode: http.StatusNotFound, - expectError: true, errorContains: "404", }, } @@ -171,49 +143,30 @@ func TestGetLimit(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - t.Errorf("expected GET request, got %s", r.Method) - } - - authHeader := r.Header.Get("Authorization") - if !strings.HasPrefix(authHeader, "Bearer ") { - t.Error("expected Authorization header with Bearer token") - } + assert.Equal(t, r.Method, "GET") + assert.Equal(t, r.Header.Get("Authorization"), "Bearer token") + assert.Contains(t, r.URL.Path, "/limit") - if !strings.Contains(r.URL.Path, "/limit") { - t.Errorf("expected path to contain /limit, got %s", r.URL.Path) - } - - w.WriteHeader(tc.mockStatusCode) if tc.mockStatusCode == http.StatusOK { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(tc.mockStatusCode) json.NewEncoder(w).Encode(tc.mockResponse) } else { + w.WriteHeader(tc.mockStatusCode) w.Write([]byte("error message")) } })) defer mockServer.Close() - cfg := newTestConfig(mockServer.URL) - - limit, err := GetLimit(context.Background(), cfg) + client, _ := schema.NewInternxtClient(mockServer.URL, "token") + limit, err := GetLimit(context.Background(), client) - if tc.expectError { - if err == nil { - t.Error("expected error, got nil") - } - if tc.errorContains != "" && !strings.Contains(err.Error(), tc.errorContains) { - t.Errorf("expected error to contain %q, got %q", tc.errorContains, err.Error()) - } + if tc.errorContains != "" { + assert.ErrorContains(t, err, tc.errorContains) } else { - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if limit == nil { - t.Fatal("expected limit response, got nil") - } - if limit.MaxSpaceBytes != tc.mockResponse.MaxSpaceBytes { - t.Errorf("expected MaxSpaceBytes %d, got %d", tc.mockResponse.MaxSpaceBytes, limit.MaxSpaceBytes) - } + assert.NoError(t, err) + assert.NotNil(t, limit) + assert.Equal(t, tc.mockResponse.MaxSpaceBytes, limit.MaxSpaceBytes) } }) } @@ -221,44 +174,20 @@ func TestGetLimit(t *testing.T) { func TestGetLimitInvalidJSON(t *testing.T) { mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte("invalid json response")) })) defer mockServer.Close() - cfg := newTestConfig(mockServer.URL) + client, _ := schema.NewInternxtClient(mockServer.URL, "token") + _, err := GetLimit(context.Background(), client) - _, err := GetLimit(context.Background(), cfg) - if err == nil { - t.Fatal("expected error for invalid JSON, got nil") - } - if !strings.Contains(err.Error(), "failed to decode") { - t.Errorf("expected error to contain 'failed to decode', got %q", err.Error()) - } -} - -func TestGetUsageHTTPClientError(t *testing.T) { - // Use an invalid URL that will cause the HTTP client to fail - cfg := newTestConfig("http://invalid-host-that-does-not-exist-12345.local") - - _, err := GetUsage(context.Background(), cfg) - if err == nil { - t.Fatal("expected error with invalid host, got nil") - } - if !strings.Contains(err.Error(), "failed to execute") { - t.Errorf("expected error to contain 'failed to execute', got %q", err.Error()) - } + assert.ErrorContains(t, err, "failed to parse response") } func TestGetLimitHTTPClientError(t *testing.T) { - // Use an invalid URL that will cause the HTTP client to fail - cfg := newTestConfig("http://invalid-host-that-does-not-exist-12345.local") - - _, err := GetLimit(context.Background(), cfg) - if err == nil { - t.Fatal("expected error with invalid host, got nil") - } - if !strings.Contains(err.Error(), "failed to execute") { - t.Errorf("expected error to contain 'failed to execute', got %q", err.Error()) - } + client, _ := schema.NewInternxtClient("http://invalid-host-that-does-not-exist", "token") + _, err := GetLimit(context.Background(), client) + assert.ErrorContains(t, err, "no such host") } From 0309fb7eca85844eaaaac91169553f156f5f1d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Tue, 13 Jan 2026 10:01:24 +0100 Subject: [PATCH 06/10] Commit --- schema/client.go | 2 +- users/users_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/schema/client.go b/schema/client.go index a516761..961b582 100644 --- a/schema/client.go +++ b/schema/client.go @@ -7,7 +7,7 @@ import ( "github.com/internxt/rclone-adapter/config" ) -func NewInternxtClient(baseUrl, token string) (*Client, error) { +func NewOpenapiClient(baseUrl, token string) (*Client, error) { return NewClient(baseUrl, WithRequestEditorFn(func(ctx context.Context, req *http.Request) error { req.Header.Set("internxt-client", config.ClientName) diff --git a/users/users_test.go b/users/users_test.go index 3af9838..39ccdde 100644 --- a/users/users_test.go +++ b/users/users_test.go @@ -68,7 +68,7 @@ func TestGetUsage(t *testing.T) { })) defer mockServer.Close() - client, _ := schema.NewInternxtClient(mockServer.URL, "token") + client, _ := schema.NewOpenapiClient(mockServer.URL, "token") usage, err := GetUsage(context.Background(), client) if tc.errorContains != "" { @@ -90,14 +90,14 @@ func TestGetUsageInvalidJSON(t *testing.T) { })) defer mockServer.Close() - client, _ := schema.NewInternxtClient(mockServer.URL, "token") + client, _ := schema.NewOpenapiClient(mockServer.URL, "token") _, err := GetUsage(context.Background(), client) assert.ErrorContains(t, err, "failed to parse response") } func TestGetUsageHTTPClientError(t *testing.T) { - client, _ := schema.NewInternxtClient("http://invalid-host-that-does-not-exist", "token") + client, _ := schema.NewOpenapiClient("http://invalid-host-that-does-not-exist", "token") _, err := GetUsage(context.Background(), client) assert.ErrorContains(t, err, "no such host") } @@ -158,7 +158,7 @@ func TestGetLimit(t *testing.T) { })) defer mockServer.Close() - client, _ := schema.NewInternxtClient(mockServer.URL, "token") + client, _ := schema.NewOpenapiClient(mockServer.URL, "token") limit, err := GetLimit(context.Background(), client) if tc.errorContains != "" { @@ -180,14 +180,14 @@ func TestGetLimitInvalidJSON(t *testing.T) { })) defer mockServer.Close() - client, _ := schema.NewInternxtClient(mockServer.URL, "token") + client, _ := schema.NewOpenapiClient(mockServer.URL, "token") _, err := GetLimit(context.Background(), client) assert.ErrorContains(t, err, "failed to parse response") } func TestGetLimitHTTPClientError(t *testing.T) { - client, _ := schema.NewInternxtClient("http://invalid-host-that-does-not-exist", "token") + client, _ := schema.NewOpenapiClient("http://invalid-host-that-does-not-exist", "token") _, err := GetLimit(context.Background(), client) assert.ErrorContains(t, err, "no such host") } From 076fe6fd21f2386e434d9ea664dcc25eb2dd4fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Tue, 13 Jan 2026 10:11:08 +0100 Subject: [PATCH 07/10] Update users_test.go --- users/users_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/users/users_test.go b/users/users_test.go index 39ccdde..3ba590b 100644 --- a/users/users_test.go +++ b/users/users_test.go @@ -96,12 +96,6 @@ func TestGetUsageInvalidJSON(t *testing.T) { assert.ErrorContains(t, err, "failed to parse response") } -func TestGetUsageHTTPClientError(t *testing.T) { - client, _ := schema.NewOpenapiClient("http://invalid-host-that-does-not-exist", "token") - _, err := GetUsage(context.Background(), client) - assert.ErrorContains(t, err, "no such host") -} - func TestGetLimit(t *testing.T) { testCases := []struct { name string @@ -186,6 +180,12 @@ func TestGetLimitInvalidJSON(t *testing.T) { assert.ErrorContains(t, err, "failed to parse response") } +func TestGetUsageHTTPClientError(t *testing.T) { + client, _ := schema.NewOpenapiClient("http://invalid-host-that-does-not-exist", "token") + _, err := GetUsage(context.Background(), client) + assert.ErrorContains(t, err, "no such host") +} + func TestGetLimitHTTPClientError(t *testing.T) { client, _ := schema.NewOpenapiClient("http://invalid-host-that-does-not-exist", "token") _, err := GetLimit(context.Background(), client) From 602094353a36e6127ed958ed9c9fd6a00f164a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 21 Jan 2026 22:15:16 +0100 Subject: [PATCH 08/10] Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bd5304f..8aeacb8 100644 --- a/go.mod +++ b/go.mod @@ -18,5 +18,5 @@ require ( require ( github.com/stretchr/testify v1.11.1 - golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect + golang.org/x/image v0.18.0 // indirect ) From 7e13c074a4376202cf555b438d11666a76a67e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 21 Jan 2026 22:24:17 +0100 Subject: [PATCH 09/10] Commit --- go.mod | 7 ++----- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 8aeacb8..9349fca 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.0 require ( github.com/disintegration/imaging v1.6.2 github.com/oapi-codegen/runtime v1.1.2 + github.com/stretchr/testify v1.11.1 github.com/tyler-smith/go-bip39 v1.1.0 golang.org/x/crypto v0.45.0 ) @@ -13,10 +14,6 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) - -require ( - github.com/stretchr/testify v1.11.1 golang.org/x/image v0.18.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dacc96f..1da2976 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQ github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From 1394d47c7a5cbd0ea399c7127a75c0dd65d39ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Jim=C3=A9nez=20Rivera?= Date: Wed, 21 Jan 2026 22:35:47 +0100 Subject: [PATCH 10/10] Commit --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 1da2976..dacc96f 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQ github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=