From 47fa522aba2f31c64342db1d482a7ff1b42eee34 Mon Sep 17 00:00:00 2001 From: James Stephenson Date: Fri, 21 Aug 2020 08:08:51 -0400 Subject: [PATCH] Expose the Account API (RADIUS users) * Adds Account CRUD API * Enahnces `emptyStringInt` with a predicate for dynamically setting which value should be interpreted as a blank JSON payload --- fields/main.go | 3 ++ go.mod | 5 ++- go.sum | 17 +++++++++ unifi/account.generated.go | 2 +- unifi/account.go | 71 ++++++++++++++++++++++++++++++++++++++ unifi/account_test.go | 37 ++++++++++++++++++++ unifi/json.go | 8 +++++ 7 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 unifi/account.go create mode 100644 unifi/account_test.go diff --git a/fields/main.go b/fields/main.go index 13cd0b1..9652877 100644 --- a/fields/main.go +++ b/fields/main.go @@ -193,6 +193,9 @@ type %s struct { for _, name := range fieldNames { switch { + case structName == "Account" && name == "ip": + code += "\tIP string `json:\"ip,omitempty\"`\n" + continue case structName == "User" && name == "blocked": code += "\tBlocked bool `json:\"blocked,omitempty\"`\n" continue diff --git a/go.mod b/go.mod index b97e218..711b22b 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/paultyng/go-unifi go 1.12 -require github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 +require ( + github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 + github.com/tj/assert v0.0.3 +) diff --git a/go.sum b/go.sum index e4d62d5..80d3d8a 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,19 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= +github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c h1:grhR+C34yXImVGp7EzNk+DTIk+323eIUWOmEevy6bDo= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/unifi/account.generated.go b/unifi/account.generated.go index d862363..494d183 100644 --- a/unifi/account.generated.go +++ b/unifi/account.generated.go @@ -23,7 +23,7 @@ type Account struct { NoDelete bool `json:"attr_no_delete,omitempty"` NoEdit bool `json:"attr_no_edit,omitempty"` - IP string `json:"ip"` // ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^$ + IP string `json:"ip,omitempty"` Name string `json:"name,omitempty"` // ^[^"' ]+$ TunnelConfigType string `json:"tunnel_config_type,omitempty"` // vpn|802.1x|custom TunnelMediumType int `json:"tunnel_medium_type,omitempty"` // [1-9]|1[0-5]|^$ diff --git a/unifi/account.go b/unifi/account.go new file mode 100644 index 0000000..73a76b0 --- /dev/null +++ b/unifi/account.go @@ -0,0 +1,71 @@ +package unifi + +import ( + "context" + "encoding/json" + "fmt" +) + +func (dst *Account) UnmarshalJSON(b []byte) error { + type Alias Account + aux := &struct { + TunnelType emptyStringInt `json:"tunnel_type"` + TunnelMediumType emptyStringInt `json:"tunnel_medium_type"` + VLAN emptyStringInt `json:"vlan"` + + *Alias + }{ + Alias: (*Alias)(dst), + } + + err := json.Unmarshal(b, &aux) + if err != nil { + return fmt.Errorf("unable to unmarshal alias: %w", err) + } + + dst.TunnelType = int(aux.TunnelType) + dst.TunnelMediumType = int(aux.TunnelMediumType) + dst.VLAN = int(aux.VLAN) + + return nil +} + +func (dst *Account) MarshalJSON() ([]byte, error) { + type Alias Account + aux := &struct { + *Alias + + TunnelType emptyStringInt `json:"tunnel_type"` + TunnelMediumType emptyStringInt `json:"tunnel_medium_type"` + VLAN emptyStringInt `json:"vlan"` + }{ + Alias: (*Alias)(dst), + } + + aux.TunnelType = emptyStringInt(dst.TunnelType) + aux.TunnelMediumType = emptyStringInt(dst.TunnelMediumType) + aux.VLAN = emptyStringInt(dst.VLAN) + + b, err := json.Marshal(aux) + return b, err +} + +func (c *Client) ListAccount(ctx context.Context, site string) ([]Account, error) { + return c.listAccount(ctx, site) +} + +func (c *Client) GetAccount(ctx context.Context, site, id string) (*Account, error) { + return c.getAccount(ctx, site, id) +} + +func (c *Client) DeleteAccount(ctx context.Context, site, id string) error { + return c.deleteAccount(ctx, site, id) +} + +func (c *Client) CreateAccount(ctx context.Context, site string, d *Account) (*Account, error) { + return c.createAccount(ctx, site, d) +} + +func (c *Client) UpdateAccount(ctx context.Context, site string, d *Account) (*Account, error) { + return c.updateAccount(ctx, site, d) +} diff --git a/unifi/account_test.go b/unifi/account_test.go new file mode 100644 index 0000000..6c1b4d6 --- /dev/null +++ b/unifi/account_test.go @@ -0,0 +1,37 @@ +package unifi_test + +import ( + "encoding/json" + "testing" + + "github.com/paultyng/go-unifi/unifi" + "github.com/tj/assert" +) + +func TestAccountMarshalJSON(t *testing.T) { + for n, c := range map[string]struct { + expectedJSON string + acc unifi.Account + }{ + "empty strings": { + `{"vlan":"","tunnel_type":"","tunnel_medium_type":""}`, + unifi.Account{}, + }, + "response": { + `{"vlan":10,"tunnel_type":1,"tunnel_medium_type":1}`, + unifi.Account{ + VLAN: 10, + TunnelType: 1, + TunnelMediumType: 1, + }, + }, + } { + t.Run(n, func(t *testing.T) { + actual, err := json.Marshal(&c.acc) + if err != nil { + t.Fatal(err) + } + assert.JSONEq(t, c.expectedJSON, string(actual)) + }) + } +} diff --git a/unifi/json.go b/unifi/json.go index f2c1d30..9f953b2 100644 --- a/unifi/json.go +++ b/unifi/json.go @@ -31,3 +31,11 @@ func (e *emptyStringInt) UnmarshalJSON(b []byte) error { *e = emptyStringInt(i) return nil } + +func (e *emptyStringInt) MarshalJSON() ([]byte, error) { + if e == nil || *e == 0 { + return []byte(`""`), nil + } + + return []byte(strconv.Itoa(int(*e))), nil +}