Skip to content

Commit

Permalink
separate backup client package
Browse files Browse the repository at this point in the history
  • Loading branch information
Nathan Hemingway committed Dec 10, 2019
1 parent 6799690 commit c0a956a
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 39 deletions.
49 changes: 29 additions & 20 deletions backup.go → backup/client.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package goaci
package backup

import (
"archive/tar"
Expand All @@ -12,8 +12,8 @@ import (
"github.com/tidwall/gjson"
)

// Backup is a client that queries an ACI backup file.
type Backup struct {
// Client is a ACI backup file client.
type Client struct {
dns map[string]*Res
classes map[string][]*Res
}
Expand Down Expand Up @@ -70,24 +70,24 @@ func buildDn(record gjson.Result, parentDn []string, class string) ([]string, er
return append(parentDn, rn), nil
}

// NewBackup creates a new backup file client.
func NewBackup(src string) (Backup, error) {
// NewClient creates a new backup file client.
func NewClient(src string) (Client, error) {
// Open backup file
f, err := os.Open(src)
if err != nil {
return Backup{}, err
return Client{}, err
}
defer f.Close()

// Unzip backup tar.gz file
gzf, err := gzip.NewReader(f)
if err != nil {
return Backup{}, err
return Client{}, err
}
defer gzf.Close()

// Initialize client
bkup := Backup{
client := Client{
dns: make(map[string]*Res),
classes: make(map[string][]*Res),
}
Expand All @@ -100,22 +100,22 @@ func NewBackup(src string) (Backup, error) {
if err == io.EOF {
break
} else if err != nil {
return Backup{}, err
return Client{}, err
}
info := header.FileInfo()
name := info.Name()
if strings.HasSuffix(name, ".json") {
data, err := ioutil.ReadAll(tarReader)
if err != nil {
return Backup{}, err
return Client{}, err
}
bkup.addToDB(gjson.ParseBytes(data))
client.addToDB(gjson.ParseBytes(data))
}
}
return bkup, nil
return client, nil
}

func (bkup Backup) addToDB(root gjson.Result) {
func (client Client) addToDB(root gjson.Result) {
type MO struct {
object gjson.Result
parentDn []string
Expand Down Expand Up @@ -144,8 +144,8 @@ func (bkup Backup) addToDB(root gjson.Result) {
attributes := moBody.Get("attributes").Raw
template := `{"%s":{"attributes":%s}}`
json := gjson.Parse(fmt.Sprintf(template, mo.class, attributes))
bkup.dns[dn] = &json
bkup.classes[mo.class] = append(bkup.classes[mo.class], &json)
client.dns[dn] = &json
client.classes[mo.class] = append(client.classes[mo.class], &json)
}

// Add children of this MO to stack
Expand All @@ -155,18 +155,27 @@ func (bkup Backup) addToDB(root gjson.Result) {
}
}

// GetClass queries the backup for an MO class.
func (bkup Backup) GetClass(class string, mods ...func(*Req)) (Res, error) {
res, ok := bkup.classes[class]
// GetClass queries the backup file for an MO class.
func (client Client) GetClass(class string, mods ...func(*Req)) (Res, error) {
res, ok := client.classes[class]
if !ok {
return Res{}, fmt.Errorf("%s not found", class)
}
return gjson.Parse(fmt.Sprintf("%v", res)), nil
}

// GetDn queries the backup for a specific DN.
func (bkup Backup) GetDn(dn string, mods ...func(*Req)) (Res, error) {
res, ok := bkup.dns[dn]
// This returns a single object of the format:
// { "moClass":
// "attributes": {
// ...
// }
// }
//
// For unknown class types, retrieve the attributes with a wildcard:
// res.Get("*.attributes")
func (client Client) GetDn(dn string, mods ...func(*Req)) (Res, error) {
res, ok := client.dns[dn]
if !ok {
return Res{}, fmt.Errorf("%s not fund", dn)
}
Expand Down
32 changes: 16 additions & 16 deletions backup_test.go → backup/client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package goaci
package backup

import (
"fmt"
Expand All @@ -9,14 +9,14 @@ import (
)

// Memoize backup client to only read the testdata once.
func newTestBackup() func() (Backup, error) {
bkup, err := NewBackup("./testdata/config.tar.gz")
return func() (Backup, error) {
func newTestClient() func() (Client, error) {
bkup, err := NewClient("./testdata/config.tar.gz")
return func() (Client, error) {
return bkup, err
}
}

var testBackup = newTestBackup()
var testClient = newTestClient()

// A testing convenience for generating GJSON from the Body.
func (body Body) gjson() gjson.Result {
Expand Down Expand Up @@ -55,26 +55,26 @@ func TestBuildDN(t *testing.T) {
assert.Error(t, err)
}

// TestNewBackup tests the NewBackup function.
func TestNewBackup(t *testing.T) {
// TestNewClient tests the NewClient function.
func TestNewClient(t *testing.T) {
// Success use case already tested
// Missing file
_, err := NewBackup("non.existent.file")
_, err := NewClient("non.existent.file")
assert.Error(t, err)

// Not a gzip file
_, err = NewBackup("./testdata/config.json")
_, err = NewClient("./testdata/config.json")
assert.Error(t, err)

// Valid gzip; invalid tar
_, err = NewBackup("./testdata/valid.gz.invalid.tar")
_, err = NewClient("./testdata/valid.gz.invalid.tar")
assert.Error(t, err)

}

// TestBackupGetDn tests the Backup::GetDn method.
func TestBackupGetDn(t *testing.T) {
bkup, _ := testBackup()
// TestClientGetDn tests the Client::GetDn method.
func TestClientGetDn(t *testing.T) {
bkup, _ := testClient()

// Valid dn
res, _ := bkup.GetDn("uni/tn-a")
Expand All @@ -87,9 +87,9 @@ func TestBackupGetDn(t *testing.T) {
assert.Error(t, err)
}

// TestBackupGetClass test the Backup::GetClass method.
func TestBackupGetClass(t *testing.T) {
bkup, _ := testBackup()
// TestClientGetClass test the Client::GetClass method.
func TestClientGetClass(t *testing.T) {
bkup, _ := testClient()

// Valid class
res, err := bkup.GetClass("fvTenant")
Expand Down
27 changes: 27 additions & 0 deletions backup/req.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package backup

import (
"github.com/tidwall/sjson"
)

// Body wraps SJSON for building JSON body strings.
type Body struct {
Str string
}

// Set sets a JSON path to a value.
func (body Body) Set(path, value string) Body {
res, _ := sjson.Set(body.Str, path, value)
body.Str = res
return body
}

// SetRaw sets a JSON path to a raw string value.
func (body Body) SetRaw(path, rawValue string) Body {
res, _ := sjson.SetRaw(body.Str, path, rawValue)
body.Str = res
return body
}

// Req
type Req struct{}
13 changes: 13 additions & 0 deletions backup/req_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package backup

import (
"testing"

"github.com/stretchr/testify/assert"
)

// TestSetRaw tests the Body::SetRaw method.
func TestSetRaw(t *testing.T) {
name := Body{}.SetRaw("a", `{"name":"a"}`).gjson().Get("a.name").Str
assert.Equal(t, "a", name)
}
8 changes: 8 additions & 0 deletions backup/res.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package backup

import (
"github.com/tidwall/gjson"
)

// Res is an API response returned by client requests.
type Res = gjson.Result
2 changes: 1 addition & 1 deletion rns.go → backup/rns.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package goaci
package backup

// Indus RN schema

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions examples/backup/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package main
import (
"fmt"

"github.com/brightpuddle/goaci"
"github.com/brightpuddle/goaci/backup"
)

func main() {
client, err := goaci.NewBackup("config.tar.gz")
client, err := backup.NewClient("config.tar.gz")
if err != nil {
panic(err)
}
Expand Down
5 changes: 5 additions & 0 deletions req_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"gopkg.in/h2non/gock.v1"
)

func (body Body) gjson() gjson.Result {
return gjson.Parse(body.Str)
}

// TestSetRaw tests the Body::SetRaw method.
func TestSetRaw(t *testing.T) {
name := Body{}.SetRaw("a", `{"name":"a"}`).gjson().Get("a.name").Str
Expand Down

0 comments on commit c0a956a

Please sign in to comment.