Skip to content

Commit 8ae1722

Browse files
Add crud.BackingStore (#176)
* Add crud.BackingStore BackingStore is a utility for people developing CRUD stores that makes it easier for them to do late binding / initialization when the list/save/delete/read methods are called, instead of in the constructor, when you may not be ready to connect to databases, plugins, etc or returning errors. Signed-off-by: Carolyn Van Slyck <carolyn.vanslyck@microsoft.com> * Finish comment sentence Signed-off-by: Carolyn Van Slyck <carolyn.vanslyck@microsoft.com> * Incorporate review feedback Signed-off-by: Carolyn Van Slyck <carolyn.vanslyck@microsoft.com> * Take into account item type in the backing store Now that #175 has been merged into master, update BackingStore to account for bucketing by item type (credentials/claims). Signed-off-by: Carolyn Van Slyck <carolyn.vanslyck@microsoft.com>
1 parent d9edf60 commit 8ae1722

File tree

7 files changed

+536
-27
lines changed

7 files changed

+536
-27
lines changed

claim/claimstore.go

+15-12
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,27 @@ package claim
33
import (
44
"encoding/json"
55
"errors"
6+
"fmt"
67

78
"github.com/cnabio/cnab-go/utils/crud"
89
)
910

11+
// ItemType is the location in the backing store where claims are persisted.
1012
const ItemType = "claims"
1113

1214
// ErrClaimNotFound represents a claim not found in claim storage
1315
var ErrClaimNotFound = errors.New("Claim does not exist")
1416

1517
// Store is a persistent store for claims.
1618
type Store struct {
17-
backingStore crud.Store
19+
backingStore *crud.BackingStore
1820
}
1921

2022
// NewClaimStore creates a persistent store for claims using the specified
2123
// backing key-blob store.
22-
func NewClaimStore(backingStore crud.Store) Store {
24+
func NewClaimStore(store crud.Store) Store {
2325
return Store{
24-
backingStore: backingStore,
26+
backingStore: crud.NewBackingStore(store),
2527
}
2628
}
2729

@@ -54,22 +56,23 @@ func (s Store) Read(name string) (Claim, error) {
5456
return claim, err
5557
}
5658

57-
// ReadAll retrieves all the claims
59+
// ReadAll retrieves all of the claims.
5860
func (s Store) ReadAll() ([]Claim, error) {
59-
claims := make([]Claim, 0)
60-
61-
list, err := s.backingStore.List(ItemType)
61+
results, err := s.backingStore.ReadAll(ItemType)
6262
if err != nil {
63-
return claims, err
63+
return nil, err
6464
}
6565

66-
for _, c := range list {
67-
cl, err := s.Read(c)
66+
claims := make([]Claim, len(results))
67+
for i, bytes := range results {
68+
var claim Claim
69+
err = json.Unmarshal(bytes, &claim)
6870
if err != nil {
69-
return claims, err
71+
return nil, fmt.Errorf("error unmarshaling claim: %v", err)
7072
}
71-
claims = append(claims, cl)
73+
claims[i] = claim
7274
}
75+
7376
return claims, nil
7477
}
7578

credentials/credstore.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package credentials
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/cnabio/cnab-go/utils/crud"
9+
)
10+
11+
// ItemType is the location in the backing store where credentials are persisted.
12+
const ItemType = "credentials"
13+
14+
// ErrNotFound represents a credential set not found in storage
15+
var ErrNotFound = errors.New("Credential set does not exist")
16+
17+
// Store is a persistent store for credential sets.
18+
type Store struct {
19+
backingStore *crud.BackingStore
20+
}
21+
22+
// NewCredentialStore creates a persistent store for credential sets using the specified
23+
// backing key-blob store.
24+
func NewCredentialStore(store crud.Store) Store {
25+
return Store{
26+
backingStore: crud.NewBackingStore(store),
27+
}
28+
}
29+
30+
// List lists the names of the stored credential sets.
31+
func (s Store) List() ([]string, error) {
32+
return s.backingStore.List(ItemType)
33+
}
34+
35+
// Save a credential set. Any previous version of the credential set is overwritten.
36+
func (s Store) Save(cred CredentialSet) error {
37+
bytes, err := json.MarshalIndent(cred, "", " ")
38+
if err != nil {
39+
return err
40+
}
41+
return s.backingStore.Save(ItemType, cred.Name, bytes)
42+
}
43+
44+
// Read loads the credential set with the given name from the store.
45+
func (s Store) Read(name string) (CredentialSet, error) {
46+
bytes, err := s.backingStore.Read(ItemType, name)
47+
if err != nil {
48+
if err == crud.ErrRecordDoesNotExist {
49+
return CredentialSet{}, ErrNotFound
50+
}
51+
return CredentialSet{}, err
52+
}
53+
credset := CredentialSet{}
54+
err = json.Unmarshal(bytes, &credset)
55+
return credset, err
56+
}
57+
58+
// ReadAll retrieves all the credential sets.
59+
func (s Store) ReadAll() ([]CredentialSet, error) {
60+
results, err := s.backingStore.ReadAll(ItemType)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
creds := make([]CredentialSet, len(results))
66+
for i, bytes := range results {
67+
var cs CredentialSet
68+
err = json.Unmarshal(bytes, &cs)
69+
if err != nil {
70+
return nil, fmt.Errorf("error unmarshaling credential set: %v", err)
71+
}
72+
creds[i] = cs
73+
}
74+
75+
return creds, nil
76+
}
77+
78+
// Delete deletes a credential set from the store.
79+
func (s Store) Delete(name string) error {
80+
return s.backingStore.Delete(ItemType, name)
81+
}

utils/crud/backingstore.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package crud
2+
3+
var _ Store = &BackingStore{}
4+
5+
// BackingStore wraps another store that may have Connect/Close methods that
6+
// need to be called.
7+
// - Connect is called before a method when the connection is closed.
8+
// - Close is called after each method when AutoClose is true (default).
9+
type BackingStore struct {
10+
AutoClose bool
11+
opened bool
12+
backingStore Store
13+
}
14+
15+
func NewBackingStore(store Store) *BackingStore {
16+
return &BackingStore{
17+
AutoClose: true,
18+
backingStore: store,
19+
}
20+
}
21+
22+
func (s *BackingStore) Connect() error {
23+
if s.opened {
24+
return nil
25+
}
26+
if connectable, ok := s.backingStore.(HasConnect); ok {
27+
s.opened = true
28+
return connectable.Connect()
29+
}
30+
return nil
31+
}
32+
33+
func (s *BackingStore) Close() error {
34+
if closable, ok := s.backingStore.(HasClose); ok {
35+
s.opened = false
36+
return closable.Close()
37+
}
38+
return nil
39+
}
40+
41+
func (s *BackingStore) autoClose() error {
42+
if s.opened && s.AutoClose {
43+
return s.Close()
44+
}
45+
return nil
46+
}
47+
48+
func (s *BackingStore) List(itemType string) ([]string, error) {
49+
err := s.Connect()
50+
if err != nil {
51+
return nil, err
52+
}
53+
54+
defer s.autoClose()
55+
56+
return s.backingStore.List(itemType)
57+
}
58+
59+
func (s *BackingStore) Save(itemType string, name string, data []byte) error {
60+
err := s.Connect()
61+
if err != nil {
62+
return err
63+
}
64+
65+
defer s.autoClose()
66+
67+
return s.backingStore.Save(itemType, name, data)
68+
}
69+
70+
func (s *BackingStore) Read(itemType string, name string) ([]byte, error) {
71+
err := s.Connect()
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
defer s.autoClose()
77+
78+
return s.backingStore.Read(itemType, name)
79+
}
80+
81+
// ReadAll retrieves all the items.
82+
func (s *BackingStore) ReadAll(itemType string) ([][]byte, error) {
83+
defer s.autoClose()
84+
85+
autoClose := s.AutoClose
86+
s.AutoClose = false
87+
defer func() { s.AutoClose = autoClose }()
88+
89+
results := make([][]byte, 0)
90+
list, err := s.List(itemType)
91+
if err != nil {
92+
return results, err
93+
}
94+
95+
for _, name := range list {
96+
result, err := s.Read(itemType, name)
97+
if err != nil {
98+
return results, err
99+
}
100+
results = append(results, result)
101+
}
102+
103+
return results, nil
104+
}
105+
106+
func (s *BackingStore) Delete(itemType string, name string) error {
107+
err := s.Connect()
108+
if err != nil {
109+
return err
110+
}
111+
112+
defer s.autoClose()
113+
114+
return s.backingStore.Delete(itemType, name)
115+
}

0 commit comments

Comments
 (0)