rollbar/terraform-provider-rollbar

View on GitHub
client/client.go

Summary

Maintainability
A
0 mins
Test Coverage
B
86%
/*
 * Copyright (c) 2024 Rollbar, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// Package client is a client library for accessing the Rollbar API.
package client

import (
    "net/http"
    "sync"
    "time"

    "github.com/go-resty/resty/v2"
    "github.com/rs/zerolog/log"
)

// DefaultBaseURL is the default base URL for the Rollbar API.
const DefaultBaseURL = "https://api.rollbar.com"
const Version = "v1.15.1"

// RollbarAPIClient is a client for the Rollbar API.
type RollbarAPIClient struct {
    BaseURL string // Base URL for Rollbar API
    Resty   *resty.Client

    m sync.Mutex
}

// NewTestClient sets up a new Rollbar API test client.
func NewTestClient(baseURL, token string) *RollbarAPIClient {
    log.Debug().Msg("Initializing Rollbar client")

    // New Resty HTTP client
    r := resty.New()

    // Use default transport - needed for VCR
    r.SetTransport(http.DefaultTransport).
        // set timeout on http client
        SetTimeout(30 * time.Second).
        // Set retry count to 1 (try 2 times before it fails)
        SetRetryCount(1).
        SetRetryWaitTime(1 * time.Second).
        SetRetryMaxWaitTime(5 * time.Second)

    // Authentication
    if token != "" {
        r = r.SetHeaders(map[string]string{
            "X-Rollbar-Access-Token": token,
            "X-Rollbar-Terraform":    "true"})
    } else {
        log.Warn().Msg("Rollbar API token not set")
    }

    // Authentication
    if baseURL == "" {
        log.Error().Msg("Rollbar API base URL not set")
    }

    // Configure Resty to use Zerolog for logging
    r.SetLogger(restyZeroLogger{log.Logger})

    // Rollbar client
    c := RollbarAPIClient{
        Resty:   r,
        BaseURL: baseURL,
    }
    return &c
}

func (c *RollbarAPIClient) SetHeaderResource(header string) {
    c.m.Lock()
    defer c.m.Unlock()
    c.Resty.SetHeader("X-Rollbar-Terraform-Resource", header)
}

func (c *RollbarAPIClient) SetHeaderDataSource(header string) {
    c.m.Lock()
    defer c.m.Unlock()
    c.Resty.SetHeader("X-Rollbar-Terraform-DataSource", header)
}

// NewClient sets up a new Rollbar API client.
func NewClient(baseURL, token string) *RollbarAPIClient {
    log.Debug().Msg("Initializing Rollbar client")
    now := time.Now().Format(time.RFC3339Nano)
    // New Resty HTTP client
    r := resty.New()

    // Use default transport - needed for VCR
    r.SetTransport(http.DefaultTransport).
        // set timeout on http client
        SetTimeout(30 * time.Second).
        // Set retry count to 4 (try 5 times before it fails)
        SetRetryCount(4).
        SetRetryWaitTime(8 * time.Second).
        SetRetryMaxWaitTime(50 * time.Second).
        AddRetryCondition(
            func(r *resty.Response, err error) bool {
                if err != nil { // network error
                    return true
                }
                return r.StatusCode() == http.StatusNotFound ||
                    r.StatusCode() == http.StatusTooManyRequests ||
                    r.StatusCode() == http.StatusInternalServerError ||
                    r.StatusCode() == http.StatusBadGateway
            })
    // Authentication

    if token != "" {
        r = r.SetHeaders(map[string]string{
            "X-Rollbar-Access-Token":      token,
            "X-Rollbar-Terraform-Version": Version,
            "X-Rollbar-Terraform-Date":    now,
        })
    } else {
        log.Warn().Msg("Rollbar API token not set")
    }

    // Authentication
    if baseURL == "" {
        log.Error().Msg("Rollbar API base URL not set")
    }

    // Configure Resty to use Zerolog for logging
    r.SetLogger(restyZeroLogger{log.Logger})

    // Rollbar client
    c := RollbarAPIClient{
        Resty:   r,
        BaseURL: baseURL,
    }
    return &c
}

// Status represents the enabled or disabled status of an entity.
type Status string

// Possible values for status
const (
    StatusEnabled  = Status("enabled")
    StatusDisabled = Status("disabled")
)

// errorFromResponse interprets the status code of Resty response, returning nil
// on success or an appropriate error code
func errorFromResponse(resp *resty.Response) error {
    switch resp.StatusCode() {
    case http.StatusOK, http.StatusCreated:
        return nil
    case http.StatusUnauthorized:
        return ErrUnauthorized
    case http.StatusNotFound:
        return ErrNotFound
    default:
        er := resp.Error().(*ErrorResult)
        log.Error().
            Int("StatusCode", resp.StatusCode()).
            Str("Status", resp.Status()).
            Interface("ErrorResult", er).
            Send()
        return er
    }
}