diff --git a/README.md b/README.md index d976aed..db13eae 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ It currently supports the full API of Plausible, which includes: * [Breakdown Queries](#breakdown-queries) * [Site Provisioning API](#site-provisioning-api) + * [List sites](#provisioning-api-get-sites) + * [Get site](#provisioning-api-get-site) * [Get/Create Shared Links](#provisioning-api-shared-links) * [Create new sites](#provisioning-api-create-new-sites) @@ -414,6 +416,95 @@ go here to know more about how to get a token for this API: * [Plausible Docs: Site Provisioning API](https://plausible.io/docs/sites-api) +### List sites + +Gets a list of existing sites your Plausible account can access. + +```go +package main + +import ( + "fmt" + "github.com/andrerfcsantos/go-plausible/plausible" +) + +func main() { + // Create a client with an API token + // Warning: This token must have permissions to the site provisioning API + client := plausible.NewClient("") + + sites, err := client.ListSites() + + if err != nil { + // handle error + } + + fmt.Printf("Sites %s\n", sites.Sites) +} +``` + +If the response contains a lot of sites, it will be paginated. To access other pages, use the pagination options: + +```go +package main + +import ( + "fmt" + "github.com/andrerfcsantos/go-plausible/plausible" + "github.com/andrerfcsantos/go-plausible/plausible/urlmaker/pagination" +) + +func main() { + // Create a client with an API token + // Warning: This token must have permissions to the site provisioning API + client := plausible.NewClient("") + + sites, err := client.ListSites( + pagination.After("awebsite"), + pagination.Before("otherwebsite"), + pagination.Limit(20), + ) + + if err != nil { + // handle error + } + + fmt.Printf("Sites %s\n", sites.Sites) +} +``` + + + +### Get site + +Gets details of a site. Your Plausible account must have access to it. + +```go +package main + +import ( + "fmt" + "github.com/andrerfcsantos/go-plausible/plausible" +) + +func main() { + // Create a client with an API token + // Warning: This token must have permissions to the site provisioning API + client := plausible.NewClient("") + + // Get an handler to perform queries for a given site + mysite := client.Site("example.com") + + siteResult, err := mysite.Details() + + if err != nil { + // handle error + } + + fmt.Printf("Site %v\n", siteResult) +} +``` + ### Get or create Shared Links Shared Links are URLs that you can generate to give others access to your dashboards. diff --git a/plausible/client.go b/plausible/client.go index 637a162..c7f34c1 100644 --- a/plausible/client.go +++ b/plausible/client.go @@ -9,6 +9,7 @@ import ( "mime/multipart" "strings" + "github.com/andrerfcsantos/go-plausible/plausible/urlmaker/pagination" "github.com/valyala/fasthttp" ) @@ -144,7 +145,32 @@ func (c *Client) CreateNewSite(siteRequest CreateSiteRequest) (CreateSiteResult, var res CreateSiteResult err = json.Unmarshal(data, &res) if err != nil { - return CreateSiteResult{}, fmt.Errorf("error parsing shared link response: %w", err) + return CreateSiteResult{}, fmt.Errorf("error parsing create site response: %w", err) + } + + return res, nil +} + +// ListSites lists existing sites in Plausible +func (c *Client) ListSites(pagOptions ...pagination.Option) (ListSitesResult, error) { + + paginator := pagination.NewPaginator(pagOptions...) + qArgs := QueryArgsFromPaginator(paginator) + + req, err := c.acquireRequest("GET", "sites", qArgs, nil) + if err != nil { + return ListSitesResult{}, fmt.Errorf("error acquiring request: %v", err) + } + + data, err := doRequest(c.client, req) + if err != nil { + return ListSitesResult{}, fmt.Errorf("error performing request to list sites: %v", err) + } + + var res ListSitesResult + err = json.Unmarshal(data, &res) + if err != nil { + return ListSitesResult{}, fmt.Errorf("error parsing list sites response: %w", err) } return res, nil @@ -156,5 +182,5 @@ func (c *Client) PushEvent(ev EventRequest) ([]byte, error) { if err != nil { return nil, fmt.Errorf("acquiring event request from client: %w", err) } - return doRequest(s.httpClient, req) + return doRequest(c.client, req) } diff --git a/plausible/list_site_request.go b/plausible/list_site_request.go new file mode 100644 index 0000000..279fc4d --- /dev/null +++ b/plausible/list_site_request.go @@ -0,0 +1,19 @@ +package plausible + +import "github.com/andrerfcsantos/go-plausible/plausible/urlmaker/pagination" + +// SiteResult contains the details for a Site +type SiteResult struct { + // Domain of the site. + Domain string `json:"domain"` + // Timezone of the newly created site. + Timezone string `json:"timezone"` +} + +// ListSitesResult is the result of a request to list sites. +type ListSitesResult struct { + // Sites is the list of sites in a response + Sites []SiteResult `json:"sites"` + // Meta is the pagination meta information of a page + Meta pagination.Meta `json:"meta"` +} diff --git a/plausible/query_args.go b/plausible/query_args.go index 81c6627..2e2d24a 100644 --- a/plausible/query_args.go +++ b/plausible/query_args.go @@ -1,5 +1,10 @@ package plausible +import ( + "github.com/andrerfcsantos/go-plausible/plausible/urlmaker/pagination" + "strconv" +) + // QueryArgs represents a list of query arguments. type QueryArgs []QueryArg @@ -48,3 +53,20 @@ type QueryArg struct { // Value is the value for the query argument Value string } + +// QueryArgsFromPaginator takes the information on a paginator and converts it into query args +func QueryArgsFromPaginator(paginator *pagination.Paginator) QueryArgs { + queryArgs := QueryArgs{} + + if paginator.After != "" { + queryArgs.Add(QueryArg{Name: "after", Value: paginator.After}) + } + if paginator.Before != "" { + queryArgs.Add(QueryArg{Name: "before", Value: paginator.Before}) + } + if paginator.Limit != 0 { + queryArgs.Add(QueryArg{Name: "limit", Value: strconv.Itoa(paginator.Limit)}) + } + + return queryArgs +} diff --git a/plausible/site.go b/plausible/site.go index 931f3d3..cf165dd 100644 --- a/plausible/site.go +++ b/plausible/site.go @@ -62,6 +62,22 @@ func (s *Site) CurrentVisitors() (int, error) { return visitors, nil } +// Details contains information about a site +func (s *Site) Details() (SiteResult, error) { + data, err := s.doRequest("GET", fmt.Sprintf("sites/%s", s.id), nil, nil) + if err != nil { + return SiteResult{}, fmt.Errorf("error performing get request: %w", err) + } + + var res SiteResult + err = json.Unmarshal(data, &res) + if err != nil { + return SiteResult{}, fmt.Errorf("error parsing get response: %w", err) + } + + return res, nil +} + // Aggregate performs an aggregate query. // An aggregate query reports data for metrics aggregated over a period of time, // eg, "total number of visitors/pageviews for a particular day". diff --git a/plausible/urlmaker/pagination/meta.go b/plausible/urlmaker/pagination/meta.go new file mode 100644 index 0000000..4c041c2 --- /dev/null +++ b/plausible/urlmaker/pagination/meta.go @@ -0,0 +1,11 @@ +package pagination + +// Meta contains pagination information related with a response +type Meta struct { + // After is the domain id that appears before all the records in the current page + After string `json:"after"` + // Before is the domain id that appears after all the records in the current page + Before string `json:"before"` + // Limit limits the number of records in a page + Limit int `json:"limit"` +} diff --git a/plausible/urlmaker/pagination/option.go b/plausible/urlmaker/pagination/option.go new file mode 100644 index 0000000..8f976bb --- /dev/null +++ b/plausible/urlmaker/pagination/option.go @@ -0,0 +1,27 @@ +package pagination + +// Option represents a pagination option. +// It is not meant to be created directly - instead, you should get +// an Option via the helper functions, like After, Before and Limit. +type Option func(*Paginator) + +// After sets the 'after' pagination option +func After(after string) Option { + return func(p *Paginator) { + p.After = after + } +} + +// Before sets the 'before' pagination option +func Before(before string) Option { + return func(p *Paginator) { + p.Before = before + } +} + +// Limit sets the 'limit' pagination option +func Limit(limit int) Option { + return func(p *Paginator) { + p.Limit = limit + } +} diff --git a/plausible/urlmaker/pagination/paginator.go b/plausible/urlmaker/pagination/paginator.go new file mode 100644 index 0000000..b6cf6d6 --- /dev/null +++ b/plausible/urlmaker/pagination/paginator.go @@ -0,0 +1,20 @@ +package pagination + +// Paginator holds information to paginate requests +type Paginator struct { + // After is the domain id that appears before all the records in the desired page + After string + // Before is the domain id that appears after all the records in the desired page + Before string + // Limit sets the maximum records in the desired page + Limit int +} + +// NewPaginator creates a new paginator with the given options +func NewPaginator(options ...Option) *Paginator { + paginator := &Paginator{} + for _, opt := range options { + opt(paginator) + } + return paginator +}