Skip to content

Commit

Permalink
Added method to get keys by prefix
Browse files Browse the repository at this point in the history
Signed-off-by: knrt10 <tripathi.kautilya@gmail.com>
  • Loading branch information
knrt10 committed May 29, 2020
1 parent 2624d8b commit 984518a
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 30 deletions.
33 changes: 33 additions & 0 deletions api/server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package server

import (
"context"
"strings"
"time"

"github.com/golang/protobuf/ptypes/empty"
Expand Down Expand Up @@ -52,6 +53,38 @@ func (c *cache) Get(ctx context.Context, args *api.GetKey) (*api.Item, error) {
}, nil
}

// GetByPrefix method is all keys that match the prefix while providing key as args
func (c *cache) GetByPrefix(ctx context.Context, args *api.GetKey) (*api.AllItems, error) {
key := args.Key
c.mu.RLock()
defer c.mu.RUnlock()
var items []*api.Item
now := time.Now().UnixNano()
for k, v := range c.items {
if v.(Item).Expiration > 0 {
if now > v.(Item).Expiration {
continue
}
}

if strings.Contains(k.(string), key) {
items = append(items, &api.Item{
Key: k.(string),
Value: v.(Item).Object.(string),
Expiration: time.Unix(0, v.(Item).Expiration).String(),
})
}
}
// This means no keys were found, or all were expired
if len(items) < 1 {
return nil, ErrNoKey
}

return &api.AllItems{
Items: items,
}, nil
}

// GetAllItems method get all unexpired keys from the cache
func (c *cache) GetAllItems(ctx context.Context, in *empty.Empty) (*api.AllItems, error) {
c.mu.RLock()
Expand Down
61 changes: 57 additions & 4 deletions api/server/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func TestGet(t *testing.T) {
}
resp, err := c.Get(context.Background(), keyGet)
if err != nil {
t.Fatalf("Adding key Failed: %v", err)
t.Fatalf("Getting key Failed: %v", err)
}
if resp.Key != "kautilya" {
t.Errorf("handler returned unexpected body: got %v want %v",
Expand All @@ -125,6 +125,59 @@ func TestGet(t *testing.T) {
}
}

func TestGetByPrefix(t *testing.T) {
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial bufnet: %v", err)
}
defer conn.Close()
c := apis.NewCacheServiceClient(conn)

keyVal1 := &apis.Item{
Key: "prefixTest",
Value: "val1",
Expiration: "10s",
}

keyVal2 := &apis.Item{
Key: "prefixTest1",
Value: "val2",
Expiration: "10s",
}

keyVal3 := &apis.Item{
Key: "prefixTest2",
Value: "val3",
Expiration: "10s",
}

c.Add(context.Background(), keyVal1)
c.Add(context.Background(), keyVal2)
c.Add(context.Background(), keyVal3)

keyWrongPrefix := &apis.GetKey{
Key: "wrongPrefix",
}
_, err = c.GetByPrefix(context.Background(), keyWrongPrefix)
if err.Error() != "rpc error: code = Unknown desc = No key found" {
t.Errorf("No key found")
}

keyRightPrefix := &apis.GetKey{
Key: "prefixTest",
}

resp, err := c.GetByPrefix(context.Background(), keyRightPrefix)
if err != nil {
t.Fatalf("Getting key by prefix Failed: %v", err)
}
if len(resp.Items) != 3 {
t.Errorf("handler returned unexpected body: got %v want %v",
len(resp.Items), 3)
}
}

func TestGetAllItems(t *testing.T) {
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
Expand All @@ -136,7 +189,7 @@ func TestGetAllItems(t *testing.T) {

_, err = c.GetAllItems(context.Background(), &empty.Empty{})
if err != nil {
t.Fatalf("Adding key Failed: %v", err)
t.Fatalf("Getting all keys Failed: %v", err)
}
}

Expand All @@ -154,7 +207,7 @@ func TestDeleteKey(t *testing.T) {
}
resp, err := c.DeleteKey(context.Background(), keyGet)
if err != nil {
t.Fatalf("Adding key Failed: %v", err)
t.Fatalf("Deleting key Failed: %v", err)
}
if resp.Success != true {
t.Errorf("handler returned unexpected body: got %v want %v",
Expand All @@ -173,7 +226,7 @@ func TestDeleteAll(t *testing.T) {

resp, err := c.DeleteAll(context.Background(), &empty.Empty{})
if err != nil {
t.Fatalf("Adding key Failed: %v", err)
t.Fatalf("Deleting key Failed: %v", err)
}
if resp.Success != true {
t.Errorf("handler returned unexpected body: got %v want %v",
Expand Down
32 changes: 32 additions & 0 deletions examples/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,29 @@ func main() {
Expiration: "2min10s",
}

keyVal4 := &api.Item{
Key: "prefixTest",
Value: "val1",
Expiration: "10s",
}

keyVal5 := &api.Item{
Key: "prefixTest1",
Value: "val2",
Expiration: "10s",
}

keyVal6 := &api.Item{
Key: "prefixTest2",
Value: "val3",
Expiration: "10s",
}

c.Add(context.Background(), keyVal1)
c.Add(context.Background(), keyVal2)
c.Add(context.Background(), keyVal4)
c.Add(context.Background(), keyVal5)
c.Add(context.Background(), keyVal6)

addKeyRes, err := c.Add(context.Background(), keyVal3)
if err != nil {
Expand Down Expand Up @@ -80,6 +101,17 @@ func main() {
}
fmt.Println("Response from server for getting a key", getKeyRes)

// Get keys by prefix
keyGetPrefix := &api.GetKey{
Key: "prefixTest",
}

getKeyPrefixRes, err := c.GetByPrefix(context.Background(), keyGetPrefix)
if err != nil {
log.Fatalf("Error when calling Get: %s", err)
}
fmt.Println("Response from server for getting a keys by prefix", getKeyPrefixRes)

// GetAllItems
getAllKeysRes, err := c.GetAllItems(context.Background(), &empty.Empty{})
if err != nil {
Expand Down
75 changes: 56 additions & 19 deletions proto/cache-service.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions proto/cache-service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ message Success {
service CacheService {
rpc Add (Item) returns (Item);
rpc Get (GetKey) returns (Item);
rpc GetByPrefix(GetKey) returns (AllItems);
rpc GetAllItems(google.protobuf.Empty) returns (AllItems);
rpc DeleteKey(GetKey) returns (Success);
rpc DeleteAll(google.protobuf.Empty) returns (Success);
Expand Down
24 changes: 17 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ This is used to get key value pair for a particular key
func (c Cache) Get(ctx context.Context, args *api.GetKey) (*api.Item, error)
```

### GetByPrefix

Used to get all key value pairs by prefix

```go
func (c Cache) GetByPrefix(ctx context.Context, args *api.GetKey) (*api.AllItems, error)
```

### GetAllItems

Used to get all key value pairs
Expand All @@ -135,27 +143,29 @@ func (c Cache) DeleteAll(ctx context.Context, in *empty.Empty) (*api.Success, er

## Testing

After running `make build` just run `make test` to run the tests. It has **coverage of 91.2%**
After running `make build` just run `make test` to run the tests. It has **coverage of 92.7%**

```bash
go test api/server/* -v -cover -race
=== RUN TestAdd
--- PASS: TestAdd (0.04s)
--- PASS: TestAdd (0.03s)
=== RUN TestGet
--- PASS: TestGet (0.03s)
--- PASS: TestGet (0.01s)
=== RUN TestGetByPrefix
--- PASS: TestGetByPrefix (0.01s)
=== RUN TestGetAllItems
--- PASS: TestGetAllItems (0.01s)
=== RUN TestDeleteKey
--- PASS: TestDeleteKey (0.01s)
--- PASS: TestDeleteKey (0.00s)
=== RUN TestDeleteAll
--- PASS: TestDeleteAll (0.01s)
--- PASS: TestDeleteAll (0.00s)
=== RUN TestGetDeletedKey
--- PASS: TestGetDeletedKey (0.01s)
=== RUN TestDeleteKeyByExpiration
--- PASS: TestDeleteKeyByExpiration (2.01s)
PASS
coverage: 91.2% of statements
ok command-line-arguments 3.617s coverage: 91.2% of statements
coverage: 92.7% of statements
ok command-line-arguments 3.709s coverage: 92.7% of statements
```
## Example
Expand Down

0 comments on commit 984518a

Please sign in to comment.