Added pokemon encounter struct
This commit is contained in:
parent
ae7b6d0f83
commit
e51545a0c3
@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
func commandMap(cfg *config) error {
|
||||
if cfg.nextLocationsURL == nil {
|
||||
return errors.New("you're on the last page")
|
||||
}
|
||||
// if cfg.nextLocationsURL == nil {
|
||||
// return errors.New("you're on the last page")
|
||||
// }
|
||||
|
||||
locationsResp, err := cfg.pokeClient.ListLocations(cfg.nextLocationsURL)
|
||||
if err != nil {
|
||||
|
||||
@ -3,16 +3,20 @@ package pokeapi
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.eo13-dev.com/enordaz/gokedex/internal/pokecache"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
httpClient http.Client
|
||||
cache pokecache.Cache
|
||||
}
|
||||
|
||||
func NewClient(timeout time.Duration) Client {
|
||||
func NewClient(timeout, cacheInterval time.Duration) Client {
|
||||
return Client{
|
||||
httpClient: http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
cache: pokecache.NewCache(cacheInterval),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package pokeapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@ -11,6 +13,9 @@ func (c *Client) ListLocations(pageUrl *string) (RespShallowLocations, error) {
|
||||
url = *pageUrl
|
||||
}
|
||||
|
||||
data, ok := c.cache.Get(url)
|
||||
if !ok {
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return RespShallowLocations{}, err
|
||||
@ -22,9 +27,16 @@ func (c *Client) ListLocations(pageUrl *string) (RespShallowLocations, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return RespShallowLocations{}, err
|
||||
}
|
||||
c.cache.Add(url, data)
|
||||
}
|
||||
|
||||
var locationsResp RespShallowLocations
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err = decoder.Decode(&locationsResp); err != nil {
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
if err := decoder.Decode(&locationsResp); err != nil {
|
||||
return RespShallowLocations{}, err
|
||||
}
|
||||
|
||||
|
||||
12
internal/pokeapi/types_pokemons.go
Normal file
12
internal/pokeapi/types_pokemons.go
Normal file
@ -0,0 +1,12 @@
|
||||
package pokeapi
|
||||
|
||||
type RespShallowLocationExplore struct {
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Encounters []struct {
|
||||
Pokemon struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
} `json:"pokemon"`
|
||||
}
|
||||
}
|
||||
63
internal/pokecache/pokecache.go
Normal file
63
internal/pokecache/pokecache.go
Normal file
@ -0,0 +1,63 @@
|
||||
package pokecache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
entries map[string]cacheEntry
|
||||
mux *sync.RWMutex
|
||||
}
|
||||
|
||||
type cacheEntry struct {
|
||||
createdAt time.Time
|
||||
val []byte
|
||||
}
|
||||
|
||||
func NewCache(interval time.Duration) Cache {
|
||||
cache := Cache{
|
||||
entries: make(map[string]cacheEntry),
|
||||
mux: &sync.RWMutex{},
|
||||
}
|
||||
|
||||
go cache.reapLoop(interval)
|
||||
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *Cache) Add(key string, value []byte) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
c.entries[key] = cacheEntry{
|
||||
createdAt: time.Now().UTC(),
|
||||
val: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
c.mux.RLock()
|
||||
defer c.mux.RUnlock()
|
||||
|
||||
val, ok := c.entries[key]
|
||||
return val.val, ok
|
||||
}
|
||||
|
||||
func (c *Cache) reapLoop(interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
for range ticker.C {
|
||||
c.reap(time.Now().UTC(), interval)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) reap(now time.Time, last time.Duration) {
|
||||
c.mux.Lock()
|
||||
defer c.mux.Unlock()
|
||||
|
||||
for k, v := range c.entries {
|
||||
if v.createdAt.Before(now.Add(-last)) {
|
||||
delete(c.entries, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
internal/pokecache/pokecache_test.go
Normal file
60
internal/pokecache/pokecache_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package pokecache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestAddGet(t *testing.T) {
|
||||
const interval = 5 * time.Second
|
||||
cases := []struct {
|
||||
key string
|
||||
val []byte
|
||||
}{
|
||||
{
|
||||
key: "https://example.com",
|
||||
val: []byte("testdata"),
|
||||
},
|
||||
{
|
||||
key: "https://example.com/path",
|
||||
val: []byte("moretestdata"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
t.Run(fmt.Sprintf("Test case %v", i), func(t *testing.T) {
|
||||
cache := NewCache(interval)
|
||||
cache.Add(c.key, c.val)
|
||||
val, ok := cache.Get(c.key)
|
||||
if !ok {
|
||||
t.Errorf("expected to find key")
|
||||
return
|
||||
}
|
||||
if string(val) != string(c.val) {
|
||||
t.Errorf("expected to find value")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReapLoop(t *testing.T) {
|
||||
const baseTime = 5 * time.Millisecond
|
||||
const waitTime = baseTime + 5*time.Millisecond
|
||||
cache := NewCache(baseTime)
|
||||
cache.Add("https://example.com", []byte("testdata"))
|
||||
|
||||
_, ok := cache.Get("https://example.com")
|
||||
if !ok {
|
||||
t.Errorf("expected to find key")
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(waitTime)
|
||||
|
||||
_, ok = cache.Get("https://example.com")
|
||||
if ok {
|
||||
t.Errorf("expected to not fing key")
|
||||
return
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user