diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..049acc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +terraform-provider-coderabbit diff --git a/Makefile b/Makefile index 709c88d..d1b568f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build install test clean +.PHONY: build install test clean lint HOSTNAME=registry.terraform.io NAMESPACE=coderabbitai @@ -27,6 +27,9 @@ fmt: vet: go vet ./... +lint: + golangci-lint run + .PHONY: deps deps: go mod tidy diff --git a/internal/client/client.go b/internal/client/client.go index c6cccdd..7094558 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -7,6 +7,7 @@ import ( "io" "math" "net/http" + "sync" "time" ) @@ -35,6 +36,10 @@ type Client struct { GitHubToken string HTTPClient *http.Client RetryConfig RetryConfig + + // Cache for seats response (valid for single terraform run) + seatsCache *SeatsResponse + seatsCacheMu sync.RWMutex } // NewClient creates a new CodeRabbit API client @@ -153,7 +158,7 @@ func (c *Client) doRequest(method, path string, body any) ([]byte, error) { } respBody, err := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() if err != nil { lastErr = fmt.Errorf("failed to read response body: %w", err) continue @@ -204,7 +209,7 @@ func (c *Client) GetGitUserID(githubID string) (string, error) { } respBody, err := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() if err != nil { lastErr = fmt.Errorf("failed to read GitHub API response: %w", err) continue @@ -234,8 +239,26 @@ func (c *Client) GetGitUserID(githubID string) (string, error) { return "", fmt.Errorf("GitHub API request failed after %d retries: %w", c.RetryConfig.MaxRetries, lastErr) } -// GetSeats retrieves all seat assignments +// GetSeats retrieves all seat assignments (cached for the lifetime of the client) func (c *Client) GetSeats() (*SeatsResponse, error) { + // Check cache first with read lock + c.seatsCacheMu.RLock() + if c.seatsCache != nil { + cached := c.seatsCache + c.seatsCacheMu.RUnlock() + return cached, nil + } + c.seatsCacheMu.RUnlock() + + // Fetch from API with write lock + c.seatsCacheMu.Lock() + defer c.seatsCacheMu.Unlock() + + // Double-check after acquiring write lock + if c.seatsCache != nil { + return c.seatsCache, nil + } + respBody, err := c.doRequest(http.MethodGet, "/seats/", nil) if err != nil { return nil, err @@ -246,9 +269,17 @@ func (c *Client) GetSeats() (*SeatsResponse, error) { return nil, fmt.Errorf("failed to unmarshal response: %w", err) } + c.seatsCache = &seats return &seats, nil } +// InvalidateSeatsCache clears the seats cache, forcing a fresh fetch on next GetSeats call +func (c *Client) InvalidateSeatsCache() { + c.seatsCacheMu.Lock() + defer c.seatsCacheMu.Unlock() + c.seatsCache = nil +} + // AssignSeat assigns a seat to a user func (c *Client) AssignSeat(gitUserID string) error { reqBody := AssignSeatRequest{GitUserID: gitUserID} @@ -266,6 +297,9 @@ func (c *Client) AssignSeat(gitUserID string) error { return fmt.Errorf("seat assignment failed") } + // Invalidate cache since seat state changed + c.InvalidateSeatsCache() + return nil } @@ -286,6 +320,9 @@ func (c *Client) UnassignSeat(gitUserID string) error { return fmt.Errorf("seat unassignment failed") } + // Invalidate cache since seat state changed + c.InvalidateSeatsCache() + return nil }