initial commit
This commit is contained in:
282
meater.go
Normal file
282
meater.go
Normal file
@ -0,0 +1,282 @@
|
||||
package meater
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
var logger = logrus.New()
|
||||
|
||||
// credentials .
|
||||
type credentials struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
// Client .
|
||||
type Client struct {
|
||||
baseURL string
|
||||
credentials credentials
|
||||
httpClient *resty.Client
|
||||
updateInterval int
|
||||
fetchInterval int
|
||||
isAuthenticated bool
|
||||
logLevel string
|
||||
}
|
||||
|
||||
// Response .
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
Data json.RawMessage `json:"data,omitempty"`
|
||||
Meta *json.RawMessage `json:"meta,omitempty"`
|
||||
Message *string `json:"message,omitempty"`
|
||||
Help *string `json:"help,omitempty"`
|
||||
}
|
||||
|
||||
// Auth .
|
||||
type Auth struct {
|
||||
Token string `json:"token"`
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
// Devices .
|
||||
type Devices struct {
|
||||
Probes []*Probe `json:"devices,omitempty"`
|
||||
}
|
||||
|
||||
// Option .
|
||||
type Option func(*Client) error
|
||||
|
||||
// BaseURL allows overriding of API client baseURL for testing
|
||||
func BaseURL(baseURL string) Option {
|
||||
return func(c *Client) error {
|
||||
c.baseURL = baseURL
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Username .
|
||||
func Username(username string) Option {
|
||||
return func(c *Client) error {
|
||||
c.credentials.username = username
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Password .
|
||||
func Password(password string) Option {
|
||||
return func(c *Client) error {
|
||||
c.credentials.password = password
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LogLevel allows overriding of API client baseURL for testing
|
||||
func LogLevel(logLevel string) Option {
|
||||
return func(c *Client) error {
|
||||
c.logLevel = logLevel
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// auth .
|
||||
func (c *Client) auth() bool {
|
||||
|
||||
params := map[string]string{
|
||||
"email": c.credentials.username,
|
||||
"password": c.credentials.password,
|
||||
}
|
||||
reqURL := API_VERSION + apiURLs["API_LOGIN"]
|
||||
resp, err := c.execute(reqURL, POST, params, true)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
auth := Auth{}
|
||||
json.Unmarshal([]byte(resp), &auth)
|
||||
|
||||
c.httpClient.SetAuthToken(auth.Token)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// parseOptions parses the supplied options functions and returns a configured
|
||||
// *Client instance
|
||||
func (c *Client) parseOptions(opts ...Option) error {
|
||||
// Range over each options function and apply it to our API type to
|
||||
// configure it. Options functions are applied in order, with any
|
||||
// conflicting options overriding earlier calls.
|
||||
for _, option := range opts {
|
||||
err := option(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// New function creates a New Meater client
|
||||
func New(opts ...Option) (*Client, error) {
|
||||
|
||||
client := Client{
|
||||
updateInterval: 7200,
|
||||
fetchInterval: 360,
|
||||
}
|
||||
|
||||
if err := client.parseOptions(opts...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetFormatter(&logrus.TextFormatter{}) // &log.TextFormatter{} | &log.JSONFormatter{}
|
||||
|
||||
switch client.logLevel {
|
||||
case "debug":
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
case "info":
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
case "warn":
|
||||
logger.SetLevel(logrus.WarnLevel)
|
||||
case "error":
|
||||
logger.SetLevel(logrus.ErrorLevel)
|
||||
default:
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
logger.Warnf("[MEATER] Invalid log level supplied: '%s'", logger.GetLevel())
|
||||
}
|
||||
|
||||
httpClient := resty.New()
|
||||
|
||||
if client.baseURL != "" {
|
||||
httpClient.SetBaseURL(client.baseURL)
|
||||
} else {
|
||||
httpClient.SetBaseURL(API_SERVER)
|
||||
}
|
||||
httpClient.
|
||||
SetHeaders(map[string]string{
|
||||
"Accept": "application/json",
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.191030.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",
|
||||
"Accept-Language": "en-US,en;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate"})
|
||||
|
||||
client.httpClient = httpClient
|
||||
|
||||
if client.auth() {
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
return &client, nil
|
||||
}
|
||||
|
||||
// ProbesList .
|
||||
func (c *Client) GetProbes() []*Probe {
|
||||
reqURL := API_VERSION + apiURLs["API_DEVICES"]
|
||||
resp, err := c.execute(reqURL, GET, map[string]string{}, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logger.Debugf("[MEATER] PROBES: %+v", string(resp))
|
||||
devices := Devices{}
|
||||
err = json.Unmarshal(resp, &devices)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(devices.Probes) > 0 {
|
||||
for _, p := range devices.Probes {
|
||||
p.client = c
|
||||
}
|
||||
}
|
||||
|
||||
return devices.Probes
|
||||
}
|
||||
|
||||
// GetProbeByID .
|
||||
func (c *Client) GetProbeByID(pID string) *Probe {
|
||||
reqURL := API_VERSION + apiURLs["API_DEVICES"] + "/" + pID
|
||||
resp, err := c.execute(reqURL, GET, map[string]string{}, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
logger.Debugf("[MEATER] PROBE: %+v", string(resp))
|
||||
probe := Probe{client: c}
|
||||
err = json.Unmarshal(resp, &probe)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &probe
|
||||
}
|
||||
|
||||
// Exec method executes a Client instance with the API URL
|
||||
// Rate limit:
|
||||
//
|
||||
// Recommended: 2 requests per 60 seconds.
|
||||
// Maximum: 60 requests per 60 seconds.
|
||||
func (c *Client) execute(requestUrl string, method string, params map[string]string, jsonEnc bool) ([]byte, error) {
|
||||
defer timeTrack("[MEATER][TIMETRK] Executing HTTP Request", logger)
|
||||
|
||||
var resp *resty.Response
|
||||
// GET Requests
|
||||
if method == "GET" {
|
||||
resp, _ = c.httpClient.
|
||||
R().
|
||||
SetQueryParams(params).
|
||||
Get(requestUrl)
|
||||
}
|
||||
|
||||
// POST Requests
|
||||
if method == "POST" {
|
||||
if jsonEnc {
|
||||
// POST > JSON Body
|
||||
resp, _ = c.httpClient.R().
|
||||
SetBody(params).
|
||||
Post(requestUrl)
|
||||
} else {
|
||||
// POST > Form Data
|
||||
resp, _ = c.httpClient.R().
|
||||
SetFormData(params).
|
||||
Post(requestUrl)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debugf("[MEATER] HTTP OUTPUT >> %v\n", string([]byte(resp.Body())))
|
||||
|
||||
respParsed := Response{}
|
||||
json.Unmarshal([]byte(resp.Body()), &respParsed)
|
||||
|
||||
if respParsed.StatusCode == 200 {
|
||||
c.isAuthenticated = true
|
||||
return respParsed.Data, nil
|
||||
} else {
|
||||
c.isAuthenticated = false
|
||||
switch error := respParsed.Status; error {
|
||||
case apiErrors["API_ERROR_NOT_FOUND"]:
|
||||
logger.Debugf("[MEATER] Not Found")
|
||||
return nil, errors.New("not found")
|
||||
case apiErrors["API_ERROR_UNAUTHORIZED"]:
|
||||
logger.Debugf("[MEATER] Client authentication failed")
|
||||
return nil, errors.New("client authentication failed")
|
||||
case apiErrors["API_ERROR_BAD_REQUEST"]:
|
||||
logger.Debugf("[MEATER] Bad Request")
|
||||
return nil, errors.New("bad request")
|
||||
case apiErrors["API_ERROR_TOO_MANY_REQUESTS"]:
|
||||
logger.Debugf("[MEATER] Too Many Requests")
|
||||
return nil, errors.New("too many requests")
|
||||
case apiErrors["API_ERROR_INTERNAL_SERVER_ERROR"]:
|
||||
logger.Debugf("[MEATER] Internal Server Error")
|
||||
return nil, errors.New("internal server error")
|
||||
default:
|
||||
logger.Debugf("[MEATER] Uknown error")
|
||||
return nil, errors.New("uknown error")
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user