feat: add API v2 support by adding APGroup and DNSRecord resource handling with generated code (#23)
* feat: add API v2 support by adding APGroup and DNSRecord resource handling with generated code * fix tests
This commit is contained in:
committed by
GitHub
parent
dca894e8e7
commit
aa188a6faa
119
codegen/apiv2.go.tmpl
Normal file
119
codegen/apiv2.go.tmpl
Normal file
@@ -0,0 +1,119 @@
|
||||
{{- $structName := .StructName }}
|
||||
|
||||
{{ define "field" }}
|
||||
{{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .FieldType }} `json:"{{ .JSONName }}{{ if .OmitEmpty }},omitempty{{ end }}"{{if .FieldValidation }} {{ .FieldValidation }}{{ end }}` {{ if .FieldValidationComment }}// {{ .FieldValidationComment }}{{ end }} {{- end }}
|
||||
{{ define "field-customUnmarshalType" }}
|
||||
{{- if eq .CustomUnmarshalType "" }}{{else}}
|
||||
{{ .FieldName }} {{ if .IsArray }}[]{{end}}{{ .CustomUnmarshalType }} `json:"{{ .JSONName }}"`{{ end }} {{- end }}
|
||||
{{ define "typecast" }}
|
||||
{{- if ne .CustomUnmarshalFunc "" }}
|
||||
dst.{{ .FieldName }}= {{ .CustomUnmarshalFunc }}(aux.{{ .FieldName }})
|
||||
{{- else if eq .CustomUnmarshalType "" }}{{else}}
|
||||
{{- if .IsArray }}
|
||||
dst.{{ .FieldName }}= make([]{{ .FieldType }}, len(aux.{{ .FieldName }}))
|
||||
for i, v := range aux.{{ .FieldName }} {
|
||||
dst.{{ .FieldName }}[i] = {{ .FieldType }}(v)
|
||||
}
|
||||
{{- else }}
|
||||
dst.{{ .FieldName }} = {{ .FieldType }}(aux.{{ .FieldName }})
|
||||
{{- end }}{{- end }}{{- end }}
|
||||
// Code generated from ace.jar fields *.json files
|
||||
// DO NOT EDIT.
|
||||
|
||||
package unifi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// just to fix compile issues with the import
|
||||
var (
|
||||
_ context.Context
|
||||
_ fmt.Formatter
|
||||
_ json.Marshaler
|
||||
)
|
||||
|
||||
{{ range $k, $v := .Types }}
|
||||
type {{ $k }} struct {
|
||||
{{ range $fk, $fv := $v.Fields }}{{ if not $fv }}
|
||||
{{ else }}{{- template "field" $fv }}{{ end }}{{ end }}
|
||||
}
|
||||
|
||||
func (dst *{{ $k }}) UnmarshalJSON(b []byte) error {
|
||||
type Alias {{ $k }}
|
||||
aux := &struct {
|
||||
{{- range $fk, $fv := $v.Fields }}{{ if not $fv }}
|
||||
{{- else }}{{- template "field-customUnmarshalType" $fv }}{{ end }}{{- end }}
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(dst),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(b, &aux)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal alias: %w", err)
|
||||
}
|
||||
|
||||
{{- range $fk, $fv := $v.Fields }}{{ if not $fv }}
|
||||
{{- else }}{{- template "typecast" $fv }}{{ end }}{{ end }}
|
||||
|
||||
return nil
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
func (c *client) list{{ .StructName }}(ctx context.Context, site string) ([]{{ .StructName }}, error) {
|
||||
var respBody []{{ .StructName }}
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/{{ .ResourcePath }}", c.apiPaths.ApiV2Path, site), nil, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) get{{ .StructName }}(ctx context.Context, site, id string) (*{{ .StructName }}, error) {
|
||||
var respBody {{ .StructName }}
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/{{ .ResourcePath }}/%s", c.apiPaths.ApiV2Path, site, id), nil, &respBody)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if respBody.ID == "" {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) delete{{ .StructName }}(ctx context.Context, site, id string) error {
|
||||
err := c.Delete(ctx, fmt.Sprintf("%s/site/%s/{{ .ResourcePath }}/%s", c.apiPaths.ApiV2Path, site, id), struct{}{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) create{{ .StructName }}(ctx context.Context, site string, d *{{ .StructName }}) (*{{ .StructName }}, error) {
|
||||
var respBody {{ .StructName }}
|
||||
|
||||
err := c.Post(ctx, fmt.Sprintf("%s/site/%s/{{ .ResourcePath }}", c.apiPaths.ApiV2Path, site), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) update{{ .StructName }}(ctx context.Context, site string, d *{{ .StructName }}) (*{{ .StructName }}, error) {
|
||||
var respBody {{ .StructName }}
|
||||
|
||||
err := c.Put(ctx, fmt.Sprintf("%s/site/%s/{{ .ResourcePath }}/%s", c.apiPaths.ApiV2Path, site, d.ID), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
@@ -301,6 +301,8 @@ customizations:
|
||||
omitEmpty: true
|
||||
NetworkID:
|
||||
omitEmpty: true
|
||||
APGroup:
|
||||
resourcePath: "apgroups"
|
||||
ChannelPlan:
|
||||
fields:
|
||||
Channel:
|
||||
@@ -312,6 +314,8 @@ customizations:
|
||||
TxPower:
|
||||
ifFieldType: "string"
|
||||
customUnmarshalType: "numberOrString"
|
||||
DNSRecord:
|
||||
resourcePath: "static-dns"
|
||||
Device:
|
||||
fields:
|
||||
_all:
|
||||
|
||||
@@ -26,6 +26,7 @@ type Generate struct {
|
||||
type ResourceCustomization struct {
|
||||
ResourceName string `yaml:"-"`
|
||||
Fields map[string]*FieldCustomization `yaml:"fields"`
|
||||
ResourcePath string `yaml:"resourcePath"`
|
||||
}
|
||||
|
||||
type ClientCustomization struct {
|
||||
@@ -75,6 +76,9 @@ func (r *ResourceCustomization) ApplyTo(resource *Resource) {
|
||||
}
|
||||
return currentProcessor(name, f)
|
||||
}
|
||||
if r.ResourcePath != "" {
|
||||
resource.ResourcePath = r.ResourcePath
|
||||
}
|
||||
} else {
|
||||
resource.FieldProcessor = compositeCustomizationsProcessor(customizationsProcessor)
|
||||
}
|
||||
@@ -155,6 +159,9 @@ func NewCodeCustomizer(customizationsPath string) (*CodeCustomizer, error) {
|
||||
}
|
||||
|
||||
func (r *CodeCustomizer) IsExcludedFromClient(resourceName string) bool {
|
||||
if r.Customizations.Client == nil || r.Customizations.Client.ExcludeResources == nil {
|
||||
return false
|
||||
}
|
||||
for _, excludedResource := range r.Customizations.Client.ExcludeResources {
|
||||
prefixedAll := strings.HasPrefix(excludedResource, "*")
|
||||
suffixedAll := strings.HasSuffix(excludedResource, "*")
|
||||
|
||||
@@ -41,23 +41,32 @@ func generateCodeFromTemplate(templateName, templateContent string, toWrite any)
|
||||
}
|
||||
|
||||
// generateCode generates code for each generation source and writes it to file.
|
||||
func generateCode(fieldsDir string, outDir string, customizer CodeCustomizer) error {
|
||||
func generateCode(fieldsDir, outDir string, customizer CodeCustomizer) error {
|
||||
if _, err := ensurePath(outDir); err != nil {
|
||||
return fmt.Errorf("unable to create output directory %s: %w", outDir, err)
|
||||
}
|
||||
|
||||
generators := make([]Generatable, 0)
|
||||
resources, err := buildResourcesFromDownloadedFields(fieldsDir, customizer)
|
||||
resources, err := buildResourcesFromDownloadedFields(fieldsDir, customizer, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build resources from downloaded fields: %w", err)
|
||||
}
|
||||
|
||||
codegenPath, err := findCodegenDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find codegen directory: %w", err)
|
||||
}
|
||||
resourcesCustomV2, err := buildCustomResources(filepath.Join(codegenPath, "v2"), customizer, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build resources from downloaded fields: %w", err)
|
||||
}
|
||||
resources = append(resources, resourcesCustomV2...)
|
||||
cb := NewClientInfoBuilder()
|
||||
customizer.ApplyToClient(cb)
|
||||
for _, resource := range resources {
|
||||
if customizer.IsExcludedFromClient(resource.Name()) {
|
||||
continue
|
||||
if !customizer.IsExcludedFromClient(resource.Name()) {
|
||||
cb.AddResource(resource)
|
||||
}
|
||||
cb.AddResource(resource)
|
||||
customizer.ApplyToResource(resource)
|
||||
generators = append(generators, resource)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ type Resource struct {
|
||||
ResourcePath string
|
||||
Types map[string]*FieldInfo
|
||||
FieldProcessor FieldProcessor
|
||||
V2 bool
|
||||
}
|
||||
|
||||
func (r *Resource) IsV2() bool {
|
||||
return r.V2
|
||||
}
|
||||
|
||||
func (r *Resource) BaseType() *FieldInfo {
|
||||
@@ -294,7 +299,13 @@ func (r *Resource) processJSON(b []byte) error {
|
||||
//go:embed api.go.tmpl
|
||||
var apiGoTemplate string
|
||||
|
||||
//go:embed apiv2.go.tmpl
|
||||
var apiGoV2Template string
|
||||
|
||||
func (r *Resource) GenerateCode() (string, error) {
|
||||
if r.IsV2() {
|
||||
return generateCodeFromTemplate("apiv2.go.tmpl", apiGoV2Template, r)
|
||||
}
|
||||
return generateCodeFromTemplate("api.go.tmpl", apiGoTemplate, r)
|
||||
}
|
||||
|
||||
@@ -320,7 +331,7 @@ func normalizeValidation(re string) string {
|
||||
|
||||
var skippable = []string{"AuthenticationRequest.json", "Setting.json", "Wall.json"}
|
||||
|
||||
func buildResourcesFromDownloadedFields(fieldsDir string, customizer CodeCustomizer) ([]*Resource, error) {
|
||||
func buildResourcesFromDownloadedFields(fieldsDir string, customizer CodeCustomizer, v2 bool) ([]*Resource, error) {
|
||||
fieldsFiles, err := os.ReadDir(fieldsDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read fields directory %s: %w", fieldsDir, err)
|
||||
@@ -349,7 +360,7 @@ func buildResourcesFromDownloadedFields(fieldsDir string, customizer CodeCustomi
|
||||
}
|
||||
|
||||
resource := NewResource(structName, urlPath)
|
||||
customizeResource(resource)
|
||||
customizeResource(resource, v2)
|
||||
customizer.ApplyToResource(resource)
|
||||
|
||||
err = resource.processJSON(b)
|
||||
@@ -362,6 +373,10 @@ func buildResourcesFromDownloadedFields(fieldsDir string, customizer CodeCustomi
|
||||
return resources, nil
|
||||
}
|
||||
|
||||
func buildCustomResources(dir string, customizer CodeCustomizer, v2 bool) ([]*Resource, error) {
|
||||
return buildResourcesFromDownloadedFields(dir, customizer, v2)
|
||||
}
|
||||
|
||||
func customizeBaseType(resource *Resource) {
|
||||
baseType := resource.BaseType()
|
||||
if resource.IsSetting() {
|
||||
@@ -395,8 +410,11 @@ func customizeBaseType(resource *Resource) {
|
||||
}
|
||||
}
|
||||
|
||||
func customizeResource(resource *Resource) {
|
||||
func customizeResource(resource *Resource, v2 bool) {
|
||||
customizeBaseType(resource)
|
||||
if v2 {
|
||||
resource.V2 = true
|
||||
}
|
||||
|
||||
switch resource.StructName {
|
||||
case "SettingGlobalAp":
|
||||
|
||||
@@ -354,7 +354,7 @@ func TestBuildResourcesFromDownloadedFields(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := assert.New(t)
|
||||
resources, err := buildResourcesFromDownloadedFields(tc.dir, CodeCustomizer{})
|
||||
resources, err := buildResourcesFromDownloadedFields(tc.dir, CodeCustomizer{}, false)
|
||||
if tc.errorContains != "" {
|
||||
require.ErrorContains(t, err, tc.errorContains)
|
||||
a.Nil(resources)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// ensurePath checks if a path exists and is a directory, if not it creates the directory. Returns true if the directories were created.
|
||||
@@ -24,3 +25,29 @@ func ensurePath(path string) (bool, error) {
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func findProjectRoot() (string, error) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Walk up the directory tree until we find a go.mod file
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(wd, "go.mod")); err == nil {
|
||||
return wd, nil
|
||||
}
|
||||
if wd == "/" {
|
||||
break
|
||||
}
|
||||
wd = filepath.Dir(wd)
|
||||
}
|
||||
return "", errors.New("unable to find project root")
|
||||
}
|
||||
|
||||
func findCodegenDir() (string, error) {
|
||||
root, err := findProjectRoot()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(root, "codegen"), nil
|
||||
}
|
||||
|
||||
4
codegen/v2/APGroup.json
Normal file
4
codegen/v2/APGroup.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "",
|
||||
"device_macs": [""]
|
||||
}
|
||||
10
codegen/v2/DNSRecord.json
Normal file
10
codegen/v2/DNSRecord.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"enabled": "true|false",
|
||||
"key": ".{1,256}",
|
||||
"port": "^[0-9][0-9]?$|^",
|
||||
"priority": "^[0-9][0-9]?$|^",
|
||||
"record_type": "A|AAAA|CNAME|MX|NS|PTR|SOA|SRV|TXT",
|
||||
"ttl": "^[0-9][0-9]?$|^",
|
||||
"value": ".{1,256}",
|
||||
"weight": "^[0-9][0-9]?$|^"
|
||||
}
|
||||
100
unifi/ap_group.generated.go
generated
Normal file
100
unifi/ap_group.generated.go
generated
Normal file
@@ -0,0 +1,100 @@
|
||||
// Code generated from ace.jar fields *.json files
|
||||
// DO NOT EDIT.
|
||||
|
||||
package unifi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// just to fix compile issues with the import
|
||||
var (
|
||||
_ context.Context
|
||||
_ fmt.Formatter
|
||||
_ json.Marshaler
|
||||
)
|
||||
|
||||
type APGroup struct {
|
||||
ID string `json:"_id,omitempty"`
|
||||
SiteID string `json:"site_id,omitempty"`
|
||||
|
||||
Hidden bool `json:"attr_hidden,omitempty"`
|
||||
HiddenID string `json:"attr_hidden_id,omitempty"`
|
||||
NoDelete bool `json:"attr_no_delete,omitempty"`
|
||||
NoEdit bool `json:"attr_no_edit,omitempty"`
|
||||
|
||||
DeviceMACs []string `json:"device_macs,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (dst *APGroup) UnmarshalJSON(b []byte) error {
|
||||
type Alias APGroup
|
||||
aux := &struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(dst),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(b, &aux)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal alias: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) listAPGroup(ctx context.Context, site string) ([]APGroup, error) {
|
||||
var respBody []APGroup
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/apgroups", c.apiPaths.ApiV2Path, site), nil, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) getAPGroup(ctx context.Context, site, id string) (*APGroup, error) {
|
||||
var respBody APGroup
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/apgroups/%s", c.apiPaths.ApiV2Path, site, id), nil, &respBody)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if respBody.ID == "" {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) deleteAPGroup(ctx context.Context, site, id string) error {
|
||||
err := c.Delete(ctx, fmt.Sprintf("%s/site/%s/apgroups/%s", c.apiPaths.ApiV2Path, site, id), struct{}{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) createAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
|
||||
var respBody APGroup
|
||||
|
||||
err := c.Post(ctx, fmt.Sprintf("%s/site/%s/apgroups", c.apiPaths.ApiV2Path, site), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) updateAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
|
||||
var respBody APGroup
|
||||
|
||||
err := c.Put(ctx, fmt.Sprintf("%s/site/%s/apgroups/%s", c.apiPaths.ApiV2Path, site, d.ID), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
@@ -2,94 +2,24 @@ package unifi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// just to fix compile issues with the import.
|
||||
var (
|
||||
_ fmt.Formatter
|
||||
_ context.Context
|
||||
)
|
||||
|
||||
// This is a v2 API object, so manually coded for now, need to figure out generation...
|
||||
|
||||
type APGroup struct {
|
||||
ID string `json:"_id,omitempty"`
|
||||
|
||||
Hidden bool `json:"attr_hidden,omitempty"`
|
||||
HiddenID string `json:"attr_hidden_id,omitempty"`
|
||||
NoDelete bool `json:"attr_no_delete,omitempty"`
|
||||
NoEdit bool `json:"attr_no_edit,omitempty"`
|
||||
|
||||
Name string `json:"name"`
|
||||
DeviceMACs []string `json:"device_macs"`
|
||||
}
|
||||
|
||||
func (c *client) ListAPGroup(ctx context.Context, site string) ([]APGroup, error) {
|
||||
var respBody []APGroup
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/apgroups", c.apiPaths.ApiV2Path, site), nil, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
return c.listAPGroup(ctx, site)
|
||||
}
|
||||
|
||||
// func (c *client) getWLANGroup(ctx context.Context, site, id string) (*WLANGroup, error) {
|
||||
// var respBody struct {
|
||||
// Meta `json:"Meta"`
|
||||
// Data []WLANGroup `json:"data"`
|
||||
// }
|
||||
|
||||
// err := c.Get(ctx, fmt.Sprintf("s/%s/rest/wlangroup/%s", site, id), nil, &respBody)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// if len(respBody.Data) != 1 {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
|
||||
// d := respBody.Data[0]
|
||||
// return &d, nil
|
||||
// }
|
||||
|
||||
// func (c *client) deleteWLANGroup(ctx context.Context, site, id string) error {
|
||||
// err := c.Delete(ctx, fmt.Sprintf("s/%s/rest/wlangroup/%s", site, id), struct{}{}, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func (c *client) CreateAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
|
||||
var respBody APGroup
|
||||
|
||||
err := c.Post(ctx, fmt.Sprintf("%s/site/%s/apgroups", c.apiPaths.ApiV2Path, site), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &respBody, nil
|
||||
return c.createAPGroup(ctx, site, d)
|
||||
}
|
||||
|
||||
// func (c *client) updateWLANGroup(ctx context.Context, site string, d *WLANGroup) (*WLANGroup, error) {
|
||||
// var respBody struct {
|
||||
// Meta `json:"Meta"`
|
||||
// Data []WLANGroup `json:"data"`
|
||||
// }
|
||||
func (c *client) GetAPGroup(ctx context.Context, site, id string) (*APGroup, error) {
|
||||
return c.getAPGroup(ctx, site, id)
|
||||
}
|
||||
|
||||
// err := c.Put(ctx, fmt.Sprintf("s/%s/rest/wlangroup/%s", site, d.ID), d, &respBody)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
func (c *client) DeleteAPGroup(ctx context.Context, site, id string) error {
|
||||
return c.deleteAPGroup(ctx, site, id)
|
||||
}
|
||||
|
||||
// if len(respBody.Data) != 1 {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
|
||||
// new := respBody.Data[0]
|
||||
|
||||
// return &new, nil
|
||||
// }
|
||||
func (c *client) UpdateAPGroup(ctx context.Context, site string, d *APGroup) (*APGroup, error) {
|
||||
return c.updateAPGroup(ctx, site, d)
|
||||
}
|
||||
|
||||
38
unifi/client.generated.go
generated
38
unifi/client.generated.go
generated
@@ -33,6 +33,25 @@ type Client interface {
|
||||
// Put sends a PUT request to the controller.
|
||||
Put(ctx context.Context, apiPath string, reqBody interface{}, respBody interface{}) error
|
||||
|
||||
// ==== client methods for APGroup resource ====
|
||||
|
||||
// CreateAPGroup creates a resource
|
||||
CreateAPGroup(ctx context.Context, site string, a *APGroup) (*APGroup, error)
|
||||
|
||||
// DeleteAPGroup deletes a resource
|
||||
DeleteAPGroup(ctx context.Context, site string, id string) error
|
||||
|
||||
// GetAPGroup retrieves a resource
|
||||
GetAPGroup(ctx context.Context, site string, id string) (*APGroup, error)
|
||||
|
||||
// ListAPGroup lists the resources
|
||||
ListAPGroup(ctx context.Context, site string) ([]APGroup, error)
|
||||
|
||||
// UpdateAPGroup updates a resource
|
||||
UpdateAPGroup(ctx context.Context, site string, a *APGroup) (*APGroup, error)
|
||||
|
||||
// ==== end of client methods for APGroup resource ====
|
||||
|
||||
// ==== client methods for Account resource ====
|
||||
|
||||
// CreateAccount creates a resource
|
||||
@@ -52,6 +71,25 @@ type Client interface {
|
||||
|
||||
// ==== end of client methods for Account resource ====
|
||||
|
||||
// ==== client methods for DNSRecord resource ====
|
||||
|
||||
// CreateDNSRecord creates a resource
|
||||
CreateDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error)
|
||||
|
||||
// DeleteDNSRecord deletes a resource
|
||||
DeleteDNSRecord(ctx context.Context, site string, id string) error
|
||||
|
||||
// GetDNSRecord retrieves a resource
|
||||
GetDNSRecord(ctx context.Context, site string, id string) (*DNSRecord, error)
|
||||
|
||||
// ListDNSRecord lists the resources
|
||||
ListDNSRecord(ctx context.Context, site string) ([]DNSRecord, error)
|
||||
|
||||
// UpdateDNSRecord updates a resource
|
||||
UpdateDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error)
|
||||
|
||||
// ==== end of client methods for DNSRecord resource ====
|
||||
|
||||
// ==== client methods for Device resource ====
|
||||
|
||||
// AdoptDevice adopts a device by MAC address.
|
||||
|
||||
115
unifi/dns_record.generated.go
generated
Normal file
115
unifi/dns_record.generated.go
generated
Normal file
@@ -0,0 +1,115 @@
|
||||
// Code generated from ace.jar fields *.json files
|
||||
// DO NOT EDIT.
|
||||
|
||||
package unifi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// just to fix compile issues with the import
|
||||
var (
|
||||
_ context.Context
|
||||
_ fmt.Formatter
|
||||
_ json.Marshaler
|
||||
)
|
||||
|
||||
type DNSRecord struct {
|
||||
ID string `json:"_id,omitempty"`
|
||||
SiteID string `json:"site_id,omitempty"`
|
||||
|
||||
Hidden bool `json:"attr_hidden,omitempty"`
|
||||
HiddenID string `json:"attr_hidden_id,omitempty"`
|
||||
NoDelete bool `json:"attr_no_delete,omitempty"`
|
||||
NoEdit bool `json:"attr_no_edit,omitempty"`
|
||||
|
||||
Enabled bool `json:"enabled"`
|
||||
Key string `json:"key,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256}
|
||||
Port int `json:"port,omitempty"` // ^[0-9][0-9]?$|^
|
||||
Priority int `json:"priority,omitempty"` // ^[0-9][0-9]?$|^
|
||||
RecordType string `json:"record_type,omitempty" validate:"omitempty,oneof=A AAAA CNAME MX NS PTR SOA SRV TXT"` // A|AAAA|CNAME|MX|NS|PTR|SOA|SRV|TXT
|
||||
Ttl int `json:"ttl,omitempty"` // ^[0-9][0-9]?$|^
|
||||
Value string `json:"value,omitempty" validate:"omitempty,gte=1,lte=256"` // .{1,256}
|
||||
Weight int `json:"weight,omitempty"` // ^[0-9][0-9]?$|^
|
||||
}
|
||||
|
||||
func (dst *DNSRecord) UnmarshalJSON(b []byte) error {
|
||||
type Alias DNSRecord
|
||||
aux := &struct {
|
||||
Port emptyStringInt `json:"port"`
|
||||
Priority emptyStringInt `json:"priority"`
|
||||
Ttl emptyStringInt `json:"ttl"`
|
||||
Weight emptyStringInt `json:"weight"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(dst),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(b, &aux)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal alias: %w", err)
|
||||
}
|
||||
dst.Port = int(aux.Port)
|
||||
dst.Priority = int(aux.Priority)
|
||||
dst.Ttl = int(aux.Ttl)
|
||||
dst.Weight = int(aux.Weight)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) listDNSRecord(ctx context.Context, site string) ([]DNSRecord, error) {
|
||||
var respBody []DNSRecord
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/static-dns", c.apiPaths.ApiV2Path, site), nil, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) getDNSRecord(ctx context.Context, site, id string) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, id), nil, &respBody)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if respBody.ID == "" {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) deleteDNSRecord(ctx context.Context, site, id string) error {
|
||||
err := c.Delete(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, id), struct{}{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) createDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
|
||||
err := c.Post(ctx, fmt.Sprintf("%s/site/%s/static-dns", c.apiPaths.ApiV2Path, site), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &respBody, nil
|
||||
}
|
||||
|
||||
func (c *client) updateDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
|
||||
err := c.Put(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, d.ID), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respBody, nil
|
||||
}
|
||||
@@ -1,110 +1,36 @@
|
||||
// Custom package for handling DNS records in client Controller
|
||||
|
||||
package unifi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type DNSRecord struct {
|
||||
ID string `json:"_id,omitempty"`
|
||||
SiteID string `json:"site_id,omitempty"`
|
||||
|
||||
Hidden bool `json:"attr_hidden,omitempty"`
|
||||
HiddenID string `json:"attr_hidden_id,omitempty"`
|
||||
NoDelete bool `json:"attr_no_delete,omitempty"`
|
||||
NoEdit bool `json:"attr_no_edit,omitempty"`
|
||||
|
||||
Enabled bool `json:"enabled"`
|
||||
Key string `json:"key,omitempty" validate:"required,gte=1,lte=128"` // .{1,128}
|
||||
Port int `json:"port,omitempty"`
|
||||
Priority int `json:"priority,omitempty" validate:"omitempty,gte=1,lte=128"` // .{1,128}
|
||||
RecordType string `json:"record_type,omitempty" validate:"required,oneof=A AAAA MX NS PTR SOA SRV TXT"` // A|AAAA|CNAME|MX|NS|PTR|SOA|SRV|TXT
|
||||
Ttl int `json:"ttl,omitempty"`
|
||||
Value string `json:"value,omitempty" validate:"required,gte=1,lte=256"` // .{1,256}
|
||||
Weight int `json:"weight,omitempty"`
|
||||
}
|
||||
|
||||
func (dst *DNSRecord) UnmarshalJSON(b []byte) error {
|
||||
type Alias DNSRecord
|
||||
aux := &struct {
|
||||
Port emptyStringInt `json:"port"`
|
||||
Priority emptyStringInt `json:"priority"`
|
||||
Ttl emptyStringInt `json:"ttl"`
|
||||
Weight emptyStringInt `json:"weight"`
|
||||
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(dst),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(b, &aux)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to unmarshal alias: %w", err)
|
||||
}
|
||||
dst.Port = int(aux.Port)
|
||||
dst.Ttl = int(aux.Ttl)
|
||||
dst.Weight = int(aux.Weight)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) ListDNSRecord(ctx context.Context, site string) ([]DNSRecord, error) {
|
||||
var respBody []DNSRecord
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/static-dns", c.apiPaths.ApiV2Path, site), nil, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
return c.listDNSRecord(ctx, site)
|
||||
}
|
||||
|
||||
func (c *client) GetDNSRecord(ctx context.Context, site, id string) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
|
||||
err := c.Get(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, id), nil, &respBody)
|
||||
// client-side filtering is needed, because of lack of endpoint
|
||||
records, err := c.listDNSRecord(ctx, site)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respBody.ID == "" {
|
||||
return nil, ErrNotFound
|
||||
for _, record := range records {
|
||||
if record.ID == id {
|
||||
return &record, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &respBody, nil
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func (c *client) DeleteDNSRecord(ctx context.Context, site, id string) error {
|
||||
err := c.Delete(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, id), struct{}{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return c.deleteDNSRecord(ctx, site, id)
|
||||
}
|
||||
|
||||
func (c *client) CreateDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
err := c.Post(ctx, fmt.Sprintf("%s/site/%s/static-dns", c.apiPaths.ApiV2Path, site), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respBody, nil
|
||||
return c.createDNSRecord(ctx, site, d)
|
||||
}
|
||||
|
||||
func (c *client) UpdateDNSRecord(ctx context.Context, site string, d *DNSRecord) (*DNSRecord, error) {
|
||||
var respBody DNSRecord
|
||||
|
||||
err := c.Put(ctx, fmt.Sprintf("%s/site/%s/static-dns/%s", c.apiPaths.ApiV2Path, site, d.ID), d, &respBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if len(respBody) != nil {
|
||||
// return nil, ErrNotFound
|
||||
// }
|
||||
|
||||
return &respBody, nil
|
||||
return c.updateDNSRecord(ctx, site, d)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user