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
+}