-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added client to send hits to Pirsch and short demo.
- Loading branch information
1 parent
d234d6a
commit 0787cab
Showing
5 changed files
with
220 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,10 @@ | ||
# go-sdk | ||
Golang Client SDK for Pirsch. | ||
# Pirsch Golang SDK | ||
|
||
[![GoDoc](https://godoc.org/github.com/pirsch-analytics/go-sdk?status.svg)](https://godoc.org/github.com/pirsch-analytics/go-sdk) | ||
<a href="https://discord.gg/fAYm4Cz"><img src="https://img.shields.io/discord/739184135649886288?logo=discord" alt="Chat on Discord"></a> | ||
|
||
This is the official Golang client SDK for Pirsch. For details, please check out our [documentation](https://docs.pirsch.io/). | ||
|
||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
package pirsch | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"sync" | ||
"time" | ||
) | ||
|
||
const ( | ||
defaultBaseURL = "https://api.pirsch.io" | ||
authenticationEndpoint = "/api/v1/token" | ||
hitEndpoint = "/api/v1/hit" | ||
) | ||
|
||
// Client is a client used to access the Pirsch API. | ||
type Client struct { | ||
baseURL string | ||
clientID string | ||
clientSecret string | ||
hostname string | ||
accessToken string | ||
expiresAt time.Time | ||
m sync.Mutex | ||
} | ||
|
||
// ClientConfig is used to configure the Client. | ||
type ClientConfig struct { | ||
// BaseURL is optional and can be used to configure a different host for the API. | ||
// This is usually left empty in production environments. | ||
BaseURL string | ||
} | ||
|
||
// NewClient creates a new client for given client ID, client secret, hostname, and optional configuration. | ||
// A new client ID and secret can be generated on the Pirsch dashboard. | ||
// The hostname must match the hostname you configured on the Pirsch dashboard (e.g. example.com). | ||
func NewClient(clientID, clientSecret, hostname string, config *ClientConfig) *Client { | ||
baseURL := defaultBaseURL | ||
|
||
if config != nil && config.BaseURL != "" { | ||
baseURL = config.BaseURL | ||
} | ||
|
||
return &Client{ | ||
baseURL: baseURL, | ||
clientID: clientID, | ||
clientSecret: clientSecret, | ||
hostname: hostname, | ||
} | ||
} | ||
|
||
// Hit sends a page hit to Pirsch for given http.Request. | ||
func (client *Client) Hit(r *http.Request) error { | ||
return client.performPost(client.baseURL+hitEndpoint, &Hit{ | ||
Hostname: client.hostname, | ||
URL: r.URL.String(), | ||
IP: r.RemoteAddr, | ||
CFConnectingIP: r.Header.Get("CF-Connecting-IP"), | ||
XForwardedFor: r.Header.Get("X-Forwarded-For"), | ||
Forwarded: r.Header.Get("Forwarded"), | ||
XRealIP: r.Header.Get("X-Real-IP"), | ||
UserAgent: r.Header.Get("User-Agent"), | ||
AcceptLanguage: r.Header.Get("Accept-Language"), | ||
Referrer: r.Header.Get("Referrer"), | ||
}) | ||
} | ||
|
||
func (client *Client) refreshToken() error { | ||
body := struct { | ||
ClientId string `json:"client_id"` | ||
ClientSecret string `json:"client_secret"` | ||
}{ | ||
client.clientID, | ||
client.clientSecret, | ||
} | ||
bodyJson, err := json.Marshal(&body) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
c := http.Client{} | ||
resp, err := c.Post(client.baseURL+authenticationEndpoint, "application/json", bytes.NewBuffer(bodyJson)) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
respJson := struct { | ||
AccessToken string `json:"access_token"` | ||
ExpiresAt time.Time `json:"expires_at"` | ||
}{} | ||
|
||
decoder := json.NewDecoder(resp.Body) | ||
|
||
if err := decoder.Decode(&respJson); err != nil { | ||
return err | ||
} | ||
|
||
client.m.Lock() | ||
defer client.m.Unlock() | ||
client.accessToken = respJson.AccessToken | ||
client.expiresAt = respJson.ExpiresAt | ||
return nil | ||
} | ||
|
||
func (client *Client) performPost(url string, body interface{}) error { | ||
reqBody, err := json.Marshal(body) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
req, err := http.NewRequest("POST", url, bytes.NewReader(reqBody)) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
client.setRequestHeaders(req) | ||
c := http.Client{} | ||
resp, err := c.Do(req) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
// refresh access token and retry on 401 | ||
if resp.StatusCode == http.StatusUnauthorized { | ||
if err := client.refreshToken(); err != nil { | ||
return err | ||
} | ||
|
||
client.setRequestHeaders(req) | ||
resp, err = c.Do(req) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
body, _ := ioutil.ReadAll(resp.Body) | ||
return errors.New(fmt.Sprintf("%s: received status code %d on request: %s", url, resp.StatusCode, string(body))) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (client *Client) setRequestHeaders(req *http.Request) { | ||
req.Header.Set("Authorization", "Bearer "+client.accessToken) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/pirsch-analytics/go-sdk" | ||
"log" | ||
"net/http" | ||
) | ||
|
||
const ( | ||
// Client ID, secret, and hostname for testing. | ||
// Replace them with your own. | ||
clientID = "i9OulOrSI0b5EBNQbb8vg5zfEg2zhQ5q" | ||
clientSecret = "FgwcclcvpTK75oqmbc6UxmJTXX3iCK8JcqPvz7ozzJzouc39KLEeriXQy45Myq92" | ||
hostname = "first.page" | ||
) | ||
|
||
func main() { | ||
log.Println("Visit http://localhost:1414") | ||
|
||
// Create a client for Pirsch. | ||
client := pirsch.NewClient(clientID, clientSecret, hostname, &pirsch.ClientConfig{ | ||
BaseURL: "http://localhost.com:9999", | ||
}) | ||
|
||
// Add a handler to serve a page. | ||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||
// To analyze traffic, send page hits to Pirsch. | ||
// You can control what gets send to Pirsch, in case the page was not found for example. | ||
if r.URL.Path == "/" { | ||
if err := client.Hit(r); err != nil { | ||
log.Println(err) | ||
} | ||
} | ||
|
||
w.Write([]byte("<h1>Hello from Pirsch!</h1>")) | ||
}) | ||
http.ListenAndServe(":1414", nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package pirsch | ||
|
||
// Hit are the parameters to send a page hit to Pirsch. | ||
type Hit struct { | ||
Hostname string | ||
URL string `json:"url"` | ||
IP string `json:"ip"` | ||
CFConnectingIP string `json:"cf_connecting_ip"` | ||
XForwardedFor string `json:"x_forwarded_for"` | ||
Forwarded string `json:"forwarded"` | ||
XRealIP string `json:"x_real_ip"` | ||
UserAgent string `json:"user_agent"` | ||
AcceptLanguage string `json:"accept_language"` | ||
Referrer string `json:"referrer"` | ||
} |