7 Commits

Author SHA1 Message Date
12c700a5c5 PanicOnError implemented 2025-02-02 22:33:24 +01:00
db413ce145 return error channels 2025-02-02 22:29:40 +01:00
12e379ab62 updated dependencies 2025-02-02 20:07:38 +01:00
3949597e01 use ezhttp v0.0.4 for better error handling 2025-02-02 20:03:47 +01:00
f732d0ed44 refreshBeforeExpiration introduced 2025-02-02 19:44:21 +01:00
b320c395df removed debug logs 2025-02-02 10:57:52 +01:00
921138f450 Refresh and AutoRefresh implemented 2025-02-02 10:54:39 +01:00
8 changed files with 116 additions and 16 deletions

View File

@ -8,6 +8,9 @@ import (
)
func (api *Api) GetApInfo(siteID model.SiteID, macAddress string) (*model.ApInfo, error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("GET"),
@ -24,7 +27,7 @@ func (api *Api) GetApInfo(siteID model.SiteID, macAddress string) (*model.ApInfo
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.Response[model.ApInfo]](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.Response[model.ApInfo]](resp)
if err != nil {
return nil, err
}

75
api.go
View File

@ -1,8 +1,11 @@
package omadaapi
import (
"context"
"fmt"
"net/http"
"sync"
"time"
"git.tordarus.net/tordarus/ezhttp"
"git.tordarus.net/tordarus/omada-api/model"
@ -15,6 +18,9 @@ type Api struct {
accessToken string
refreshToken string
expiration time.Time
refreshMutex sync.RWMutex
}
type ApiConfig struct {
@ -61,6 +67,7 @@ func NewApi(config ApiConfig) (*Api, error) {
return nil, fmt.Errorf("auth token request failed: %s", authTokenResponse.Message)
}
api.expiration = time.Now().Add(time.Duration(authTokenResponse.Result.ExpiresIn) * time.Second)
api.accessToken = authTokenResponse.Result.AccessToken
api.refreshToken = authTokenResponse.Result.RefreshToken
@ -73,6 +80,9 @@ func NewApi(config ApiConfig) (*Api, error) {
}
func (api *Api) Login() (*model.LoginResponse, error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
reqBody := model.LoginRequest{
Username: api.config.Username,
Password: api.config.Password,
@ -92,7 +102,7 @@ func (api *Api) Login() (*model.LoginResponse, error) {
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.LoginResponse](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.LoginResponse](resp)
if err != nil {
return nil, err
}
@ -101,6 +111,9 @@ func (api *Api) Login() (*model.LoginResponse, error) {
}
func (api *Api) AuthCode(csrfToken, sessionID string) (*model.AuthCodeResponse, error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("POST"),
@ -115,7 +128,7 @@ func (api *Api) AuthCode(csrfToken, sessionID string) (*model.AuthCodeResponse,
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.AuthCodeResponse](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.AuthCodeResponse](resp)
if err != nil {
return nil, err
}
@ -124,6 +137,9 @@ func (api *Api) AuthCode(csrfToken, sessionID string) (*model.AuthCodeResponse,
}
func (api *Api) AuthToken(authCode string) (*model.AuthTokenResponse, error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("POST"),
@ -142,10 +158,63 @@ func (api *Api) AuthToken(authCode string) (*model.AuthTokenResponse, error) {
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.AuthTokenResponse](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.AuthTokenResponse](resp)
if err != nil {
return nil, err
}
return response, nil
}
func (api *Api) MustAutoRefresh(ctx context.Context, refreshBeforeExpiration time.Duration) {
if err := api.AutoRefresh(ctx, refreshBeforeExpiration); err != nil {
panic(err)
}
}
func (api *Api) AutoRefresh(ctx context.Context, refreshBeforeExpiration time.Duration) error {
for {
select {
case <-ctx.Done():
return nil
case <-time.After(time.Until(api.expiration.Add(-refreshBeforeExpiration))):
if err := api.Refresh(); err != nil {
return err
}
}
}
}
func (api *Api) Refresh() error {
api.refreshMutex.Lock()
defer api.refreshMutex.Unlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("POST"),
ezhttp.AppendPath("/openapi/authorize/token"),
ezhttp.Query(
"client_id", api.config.ClientID,
"client_secret", api.config.ClientSecret,
"refresh_token", api.refreshToken,
"grant_type", "refresh_token",
),
)
resp, err := ezhttp.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.AuthTokenResponse](resp)
if err != nil {
return err
}
api.expiration = time.Now().Add(time.Duration(response.Result.ExpiresIn) * time.Second)
api.accessToken = response.Result.AccessToken
api.refreshToken = response.Result.RefreshToken
return nil
}

View File

@ -8,15 +8,18 @@ import (
"git.tordarus.net/tordarus/omada-api/model"
)
func (api *Api) GetClients(siteID model.SiteID) <-chan *model.Client {
func (api *Api) GetClients(siteID model.SiteID) (<-chan *model.Client, <-chan error) {
out := make(chan *model.Client, 1000)
errChan := make(chan error)
go func() {
defer close(out)
defer close(errChan)
for page := 1; ; page++ {
resp, err := api.getClients(page, siteID)
if err != nil {
errChan <- err
return
}
@ -30,10 +33,13 @@ func (api *Api) GetClients(siteID model.SiteID) <-chan *model.Client {
}
}()
return out
return out, errChan
}
func (api *Api) getClients(page int, siteID model.SiteID) (*model.PagedResponse[model.Client], error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("GET"),
@ -50,7 +56,7 @@ func (api *Api) getClients(page int, siteID model.SiteID) (*model.PagedResponse[
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Client]](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Client]](resp)
if err != nil {
return nil, err
}

View File

@ -8,15 +8,18 @@ import (
"git.tordarus.net/tordarus/omada-api/model"
)
func (api *Api) GetDevices(siteID model.SiteID) <-chan *model.Device {
func (api *Api) GetDevices(siteID model.SiteID) (<-chan *model.Device, <-chan error) {
out := make(chan *model.Device, 1000)
errChan := make(chan error)
go func() {
defer close(out)
defer close(errChan)
for page := 1; ; page++ {
resp, err := api.getDevices(page, siteID)
if err != nil {
errChan <- err
return
}
@ -30,10 +33,13 @@ func (api *Api) GetDevices(siteID model.SiteID) <-chan *model.Device {
}
}()
return out
return out, errChan
}
func (api *Api) getDevices(page int, siteID model.SiteID) (*model.PagedResponse[model.Device], error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("GET"),
@ -50,7 +56,7 @@ func (api *Api) getDevices(page int, siteID model.SiteID) (*model.PagedResponse[
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Device]](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Device]](resp)
if err != nil {
return nil, err
}

2
go.mod
View File

@ -2,4 +2,4 @@ module git.tordarus.net/tordarus/omada-api
go 1.23.0
require git.tordarus.net/tordarus/ezhttp v0.0.3
require git.tordarus.net/tordarus/ezhttp v0.0.5

4
go.sum
View File

@ -1,2 +1,2 @@
git.tordarus.net/tordarus/ezhttp v0.0.3 h1:K6IlLmqkAFUF68HJsOTKcP3ejco7qfm+MuEagohoouo=
git.tordarus.net/tordarus/ezhttp v0.0.3/go.mod h1:Zq9o0Hibny61GqSCwJHa0PfGjVoUFv/zt2PjiQHXvmY=
git.tordarus.net/tordarus/ezhttp v0.0.5 h1:pxfEdfDeOHT/ATXYy5OQHmeBIho121SBuFvU4ISQ7w0=
git.tordarus.net/tordarus/ezhttp v0.0.5/go.mod h1:Zq9o0Hibny61GqSCwJHa0PfGjVoUFv/zt2PjiQHXvmY=

12
site.go
View File

@ -8,15 +8,18 @@ import (
"git.tordarus.net/tordarus/omada-api/model"
)
func (api *Api) GetSites() <-chan *model.Site {
func (api *Api) GetSites() (<-chan *model.Site, <-chan error) {
out := make(chan *model.Site, 1000)
errChan := make(chan error)
go func() {
defer close(out)
defer close(errChan)
for page := 1; ; page++ {
resp, err := api.getSites(page)
if err != nil {
errChan <- err
return
}
@ -30,10 +33,13 @@ func (api *Api) GetSites() <-chan *model.Site {
}
}()
return out
return out, errChan
}
func (api *Api) getSites(page int) (*model.PagedResponse[model.Site], error) {
api.refreshMutex.RLock()
defer api.refreshMutex.RUnlock()
req := ezhttp.Request(
ezhttp.Template(api.tmpl),
ezhttp.Method("GET"),
@ -50,7 +56,7 @@ func (api *Api) getSites(page int) (*model.PagedResponse[model.Site], error) {
}
defer resp.Body.Close()
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Site]](resp.Body)
response, err := ezhttp.ParseJsonResponse[model.PagedResponse[model.Site]](resp)
if err != nil {
return nil, err
}

10
utils.go Normal file
View File

@ -0,0 +1,10 @@
package omadaapi
func PanicOnError[T any](valueChan <-chan T, errChan <-chan error) <-chan T {
go func() {
for err := range errChan {
panic(err)
}
}()
return valueChan
}