Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ func Execute() {
}
}
globalpingClient := globalping.NewClientWithCacheCleanup(globalping.Config{
APIURL: config.GlobalpingAPIURL,
AuthURL: config.GlobalpingAuthURL,
DashboardURL: config.GlobalpingDashboardURL,
AuthToken: token,
AuthToken: token,
OnTokenRefresh: func(token *globalping.Token) {
profile.Token = token
err := localStorage.SaveConfig()
Expand Down
36 changes: 33 additions & 3 deletions globalping/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,62 @@ import (
"time"
)

const (
GlobalpingAPIURL = "https://api.globalping.io/v1"
GlobalpingAuthURL = "https://auth.globalping.io"
GlobalpingDashboardURL = "https://dash.globalping.io"
)

type Client interface {
// Creates a new measurement with parameters set in the request body. The measurement runs asynchronously and you can retrieve its current state at the URL returned in the Location header.
//
// https://globalping.io/docs/api.globalping.io#post-/v1/measurements
CreateMeasurement(measurement *MeasurementCreate) (*MeasurementCreateResponse, error)

// Returns the status and results of an existing measurement. Measurements are typically available for up to 7 days after creation.
//
// https://globalping.io/docs/api.globalping.io#get-/v1/measurements/-id-
GetMeasurement(id string) (*Measurement, error)

// Waits for the measurement to complete and returns the results.
//
// https://globalping.io/docs/api.globalping.io#get-/v1/measurements/-id-
AwaitMeasurement(id string) (*Measurement, error)

// Returns the status and results of an existing measurement. Measurements are typically available for up to 7 days after creation.
//
// https://globalping.io/docs/api.globalping.io#get-/v1/measurements/-id-
GetMeasurementRaw(id string) ([]byte, error)

// Returns a link to be used for authorization and listens for the authorization callback.
//
// onTokenRefresh will be called if the authorization is successful.
Authorize(callback func(error)) (*AuthorizeResponse, error)

// Returns the introspection response for the token.
//
// If the token is empty, the client's current token will be used.
TokenIntrospection(token string) (*IntrospectionResponse, error)

// Removes the current token from the client. It also revokes the tokens if the refresh token is available.
//
// onTokenRefresh will be called if the token is successfully removed.
Logout() error

// Revokes the token.
RevokeToken(token string) error

// Returns the rate limits for the current user or IP address.
Limits() (*LimitsResponse, error)
}

type Config struct {
HTTPClient *http.Client // If set, this client will be used for API requests and authorization

APIURL string
DashboardURL string
APIURL string // optional
DashboardURL string // optional

AuthURL string
AuthURL string // optional
AuthClientID string
AuthClientSecret string
AuthToken *Token
Expand Down Expand Up @@ -90,13 +108,25 @@ func NewClient(config Config) Client {
userAgent: config.UserAgent,
cache: map[string]*CacheEntry{},
}

if config.APIURL == "" {
c.apiURL = GlobalpingAPIURL
}
if config.AuthURL == "" {
c.authURL = GlobalpingAuthURL
}
if config.DashboardURL == "" {
c.dashboardURL = GlobalpingDashboardURL
}

if config.HTTPClient != nil {
c.http = config.HTTPClient
} else {
c.http = &http.Client{
Timeout: 30 * time.Second,
}
}

if config.AuthToken != nil {
c.token = &Token{
AccessToken: config.AuthToken.AccessToken,
Expand Down
29 changes: 29 additions & 0 deletions globalping/measurements.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"strconv"
"time"

"github.com/andybalholm/brotli"
"github.com/jsdelivr/globalping-cli/utils"
Expand Down Expand Up @@ -161,6 +162,34 @@ func (c *client) GetMeasurement(id string) (*Measurement, error) {
return m, nil
}

func (c *client) AwaitMeasurement(id string) (*Measurement, error) {
respBytes, err := c.GetMeasurementRaw(id)
if err != nil {
return nil, err
}
m := &Measurement{}
err = json.Unmarshal(respBytes, m)
if err != nil {
return nil, &MeasurementError{
Message: fmt.Sprintf("invalid get measurement format returned: %v %s", err, string(respBytes)),
}
}
for m.Status == StatusInProgress {
time.Sleep(500 * time.Millisecond)
respBytes, err := c.GetMeasurementRaw(id)
if err != nil {
return nil, err
}
err = json.Unmarshal(respBytes, m)
if err != nil {
return nil, &MeasurementError{
Message: fmt.Sprintf("invalid get measurement format returned: %v %s", err, string(respBytes)),
}
}
}
return m, nil
}

func (c *client) GetMeasurementRaw(id string) ([]byte, error) {
req, err := http.NewRequest("GET", c.apiURL+"/measurements/"+id, nil)
if err != nil {
Expand Down
25 changes: 25 additions & 0 deletions globalping/measurements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1048,6 +1048,31 @@ func Test_GetMeasurementRaw_Json(t *testing.T) {
assert.Equal(t, `{"id":"abcd"}`, string(res))
}

func Test_AwaitMeasurement(t *testing.T) {
count := 0
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
var err error
if count == 3 {
_, err = w.Write([]byte(`{"id":"abcd", "status": "finished"}`))
} else {
_, err = w.Write([]byte(`{"id":"abcd", "status": "in-progress"}`))
}
if err != nil {
panic(err)
}
count++
}))
defer server.Close()
client := NewClient(Config{APIURL: server.URL})
res, err := client.AwaitMeasurement("abcd")
if err != nil {
t.Error(err)
}
assert.Equal(t, "abcd", res.ID)
assert.Equal(t, StatusFinished, res.Status)
}

func generateServer(json string, statusCode int) *httptest.Server {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
Expand Down
15 changes: 15 additions & 0 deletions mocks/mock_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ import (

type Config struct {
GlobalpingToken string
GlobalpingAPIURL string
GlobalpingAuthURL string
GlobalpingDashboardURL string
GlobalpingAuthClientID string
GlobalpingAuthClientSecret string
GlobalpingAPIInterval _time.Duration
}

func NewConfig() *Config {
return &Config{
GlobalpingAPIURL: "https://api.globalping.io/v1",
GlobalpingAuthURL: "https://auth.globalping.io",
GlobalpingDashboardURL: "https://dash.globalping.io",
GlobalpingAuthClientID: "be231712-03f4-45bf-9f15-023506ce0b72",
GlobalpingAuthClientSecret: "public",
GlobalpingAPIInterval: 500 * _time.Millisecond,
Expand Down
1 change: 1 addition & 0 deletions view/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

var ShareURL = "https://globalping.io?measurement="

// TODO: Use globalping.AwaitMeasurement instead of GetMeasurement
func (v *viewer) Output(id string, m *globalping.MeasurementCreate) error {
// Wait for first result to arrive from a probe before starting display (can be in-progress)
data, err := v.globalping.GetMeasurement(id)
Expand Down