ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

locations refactor not a big fan (#371)

* not a big fan

* simplify locaitons?

* a littme better

* publix

* nit picks

---------

Co-authored-by: paul miller <paul.miller>

authored by

Paul Miller
paul miller
and committed by
GitHub
e3152fdc 7a13af10

+166 -119
+14 -3
internal/albertsons/locations.go
··· 2 2 3 3 import ( 4 4 "careme/internal/cache" 5 + "careme/internal/config" 5 6 "careme/internal/locations/nearby" 6 7 locationtypes "careme/internal/locations/types" 7 8 "context" ··· 18 19 byID map[string]locationtypes.Location 19 20 } 20 21 21 - func NewLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 22 - if c == nil { 23 - return nil, fmt.Errorf("list cache is required") 22 + func NewLocationBackendFromConfig(ctx context.Context, cfg *config.Config, zipLookup centroidByZip) (*LocationBackend, error) { 23 + if !cfg.Albertsons.IsEnabled() { 24 + return nil, locationtypes.DisabledBackendError("Albertsons") 24 25 } 26 + 25 27 if zipLookup == nil { 26 28 return nil, fmt.Errorf("zip centroid lookup is required") 27 29 } 28 30 31 + listCache, err := cache.EnsureCache(Container) 32 + if err != nil { 33 + return nil, fmt.Errorf("create Albertsons list cache: %w", err) 34 + } 35 + 36 + return newLocationBackend(ctx, listCache, zipLookup) 37 + } 38 + 39 + func newLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 29 40 summaries, err := loadCachedStoreSummaries(ctx, c) 30 41 if err != nil { 31 42 return nil, err
+3 -3
internal/albertsons/locations_test.go
··· 16 16 t.Fatalf("CacheStoreSummary returned error: %v", err) 17 17 } 18 18 19 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 19 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 20 20 "98006": {Lat: 47.5750, Lon: -122.1400}, 21 21 }) 22 22 if err != nil { ··· 50 50 t.Fatalf("cache far summary: %v", err) 51 51 } 52 52 53 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 53 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 54 54 "98006": {Lat: 47.5750, Lon: -122.1400}, 55 55 }) 56 56 if err != nil { ··· 77 77 78 78 cacheStore := cache.NewInMemoryCache() 79 79 80 - _, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 80 + _, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 81 81 if err == nil { 82 82 t.Fatal("expected NewLocationBackend to return an error") 83 83 }
+14 -3
internal/aldi/locations.go
··· 2 2 3 3 import ( 4 4 "careme/internal/cache" 5 + "careme/internal/config" 5 6 "careme/internal/locations/nearby" 6 7 locationtypes "careme/internal/locations/types" 7 8 "context" ··· 18 19 byID map[string]locationtypes.Location 19 20 } 20 21 21 - func NewLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 22 - if c == nil { 23 - return nil, fmt.Errorf("list cache is required") 22 + func NewLocationBackendFromConfig(ctx context.Context, cfg *config.Config, zipLookup centroidByZip) (*LocationBackend, error) { 23 + if !cfg.Aldi.IsEnabled() { 24 + return nil, locationtypes.DisabledBackendError("ALDI") 24 25 } 25 26 if zipLookup == nil { 26 27 return nil, fmt.Errorf("zip centroid lookup is required") 27 28 } 29 + 30 + listCache, err := cache.EnsureCache(Container) 31 + if err != nil { 32 + return nil, fmt.Errorf("create ALDI list cache: %w", err) 33 + } 34 + return newLocationBackend(ctx, listCache, zipLookup) 35 + 36 + } 37 + 38 + func newLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 28 39 29 40 summaries, err := loadCachedStoreSummaries(ctx, c) 30 41 if err != nil {
+6 -6
internal/aldi/locations_test.go
··· 16 16 t.Fatalf("CacheStoreSummary returned error: %v", err) 17 17 } 18 18 19 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 19 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 20 20 "60610": {Lat: 41.9033, Lon: -87.6313}, 21 21 }) 22 22 if err != nil { 23 - t.Fatalf("NewLocationBackend returned error: %v", err) 23 + t.Fatalf("newLocationBackend returned error: %v", err) 24 24 } 25 25 26 26 if !backend.IsID("aldi_F100") { ··· 47 47 t.Fatalf("cache far summary: %v", err) 48 48 } 49 49 50 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 50 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 51 51 "60610": {Lat: 41.9033, Lon: -87.6313}, 52 52 }) 53 53 if err != nil { 54 - t.Fatalf("NewLocationBackend returned error: %v", err) 54 + t.Fatalf("newLocationBackend returned error: %v", err) 55 55 } 56 56 57 57 locs, err := backend.GetLocationsByZip(context.Background(), "60610") ··· 74 74 75 75 cacheStore := cache.NewInMemoryCache() 76 76 77 - _, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 77 + _, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 78 78 if err == nil { 79 - t.Fatal("expected NewLocationBackend to return an error") 79 + t.Fatal("expected newLocationBackend to return an error") 80 80 } 81 81 if !strings.Contains(err.Error(), "failed to load aldi locations") { 82 82 t.Fatalf("expected missing summaries error, got %v", err)
+18 -3
internal/heb/locations.go
··· 2 2 3 3 import ( 4 4 "careme/internal/cache" 5 + "careme/internal/config" 5 6 "careme/internal/locations/nearby" 6 7 locationtypes "careme/internal/locations/types" 7 8 "context" ··· 18 19 byID map[string]locationtypes.Location 19 20 } 20 21 21 - func NewLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 22 - if c == nil { 23 - return nil, fmt.Errorf("list cache is required") 22 + func NewLocationBackendFromConfig(ctx context.Context, cfg *config.Config, zipLookup centroidByZip) (*LocationBackend, error) { 23 + if !cfg.HEB.IsEnabled() { 24 + return nil, locationtypes.DisabledBackendError("HEB") 24 25 } 26 + 25 27 if zipLookup == nil { 26 28 return nil, fmt.Errorf("zip centroid lookup is required") 29 + } 30 + 31 + listCache, err := cache.EnsureCache(Container) 32 + if err != nil { 33 + return nil, fmt.Errorf("create HEB list cache: %w", err) 34 + } 35 + 36 + return newLocationBackend(ctx, listCache, zipLookup) 37 + } 38 + 39 + func newLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 40 + if c == nil { 41 + return nil, fmt.Errorf("list cache is required") 27 42 } 28 43 29 44 summaries, err := loadCachedStoreSummaries(ctx, c)
+6 -6
internal/heb/locations_test.go
··· 16 16 t.Fatalf("CacheStoreSummary returned error: %v", err) 17 17 } 18 18 19 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 19 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 20 20 "78380": {Lat: 27.8000, Lon: -97.6700}, 21 21 }) 22 22 if err != nil { 23 - t.Fatalf("NewLocationBackend returned error: %v", err) 23 + t.Fatalf("newLocationBackend returned error: %v", err) 24 24 } 25 25 26 26 if !backend.IsID("heb_22") { ··· 47 47 t.Fatalf("cache far store summary: %v", err) 48 48 } 49 49 50 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 50 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 51 51 "78380": {Lat: 27.8000, Lon: -97.6700}, 52 52 }) 53 53 if err != nil { 54 - t.Fatalf("NewLocationBackend returned error: %v", err) 54 + t.Fatalf("newLocationBackend returned error: %v", err) 55 55 } 56 56 57 57 locs, err := backend.GetLocationsByZip(context.Background(), "78380") ··· 71 71 72 72 cacheStore := cache.NewInMemoryCache() 73 73 74 - _, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 74 + _, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 75 75 if err == nil { 76 - t.Fatal("expected NewLocationBackend to return an error") 76 + t.Fatal("expected newLocationBackend to return an error") 77 77 } 78 78 if !strings.Contains(err.Error(), "failed to load heb locations") { 79 79 t.Fatalf("expected missing summaries error, got %v", err)
+18 -74
internal/locations/locations.go
··· 81 81 return mock{}, nil 82 82 } 83 83 84 - //pass these in? 85 - var backends []locationBackend 86 - kclient, err := kroger.FromConfig(cfg) 87 - if err != nil { 88 - return nil, fmt.Errorf("failed to create Kroger client: %w", err) 89 - } 90 - backends = append(backends, kclient) 91 - 92 - if cfg.Walmart.IsEnabled() { 93 - wclient, err := walmart.NewClient(cfg.Walmart) 94 - if err != nil { 95 - return nil, fmt.Errorf("failed to create Walmart client: %w", err) 96 - } 97 - backends = append(backends, wclient) 98 - } 99 - if cfg.Aldi.IsEnabled() { 100 - slog.Info("initializing ALDI location backend") 101 - listCache, err := cache.EnsureCache(aldi.Container) 102 - if err != nil { 103 - return nil, fmt.Errorf("failed to create ALDI list cache: %w", err) 104 - } 105 - 106 - aldiBackend, err := aldi.NewLocationBackend(context.Background(), listCache, centroids) 107 - if err != nil { 108 - return nil, fmt.Errorf("failed to create ALDI backend: %w", err) 109 - } 110 - backends = append(backends, aldiBackend) 111 - } 112 - if cfg.WholeFoods.IsEnabled() { 113 - slog.Info("initializing Whole Foods location backend") 114 - listCache, err := cache.EnsureCache(wholefoods.Container) 115 - if err != nil { 116 - return nil, fmt.Errorf("failed to create Whole Foods list cache: %w", err) 117 - } 118 - 119 - wfBackend, err := wholefoods.NewLocationBackend(context.Background(), listCache, centroids) 120 - if err != nil { 121 - return nil, fmt.Errorf("failed to create Whole Foods backend: %w", err) 122 - } 123 - backends = append(backends, wfBackend) 124 - } 125 - if cfg.Albertsons.IsEnabled() { 126 - slog.Info("initializing Albertsons location backend") 127 - listCache, err := cache.EnsureCache(albertsons.Container) 128 - if err != nil { 129 - return nil, fmt.Errorf("failed to create Albertsons list cache: %w", err) 130 - } 131 - 132 - albertsonsBackend, err := albertsons.NewLocationBackend(context.Background(), listCache, centroids) 133 - if err != nil { 134 - return nil, fmt.Errorf("failed to create Albertsons backend: %w", err) 135 - } 136 - backends = append(backends, albertsonsBackend) 84 + ctx := context.Background() 85 + type locationBackendFactory func() (locationBackend, error) 86 + backendfactories := []locationBackendFactory{ 87 + func() (locationBackend, error) { return kroger.FromConfig(cfg) }, 88 + func() (locationBackend, error) { return walmart.NewClient(cfg.Walmart) }, 89 + func() (locationBackend, error) { return aldi.NewLocationBackendFromConfig(ctx, cfg, centroids) }, 90 + func() (locationBackend, error) { return wholefoods.NewLocationBackendFromConfig(ctx, cfg, centroids) }, 91 + func() (locationBackend, error) { return albertsons.NewLocationBackendFromConfig(ctx, cfg, centroids) }, 92 + func() (locationBackend, error) { return publix.NewLocationBackendFromConfig(ctx, cfg, centroids) }, 93 + func() (locationBackend, error) { return heb.NewLocationBackendFromConfig(ctx, cfg, centroids) }, 137 94 } 138 - if cfg.Publix.IsEnabled() { 139 - slog.Info("initializing Publix location backend") 140 - listCache, err := cache.EnsureCache(publix.Container) 141 - if err != nil { 142 - return nil, fmt.Errorf("failed to create Publix list cache: %w", err) 143 - } 144 95 145 - publixBackend, err := publix.NewLocationBackend(context.Background(), listCache, centroids) 96 + backends := make([]locationBackend, 0, len(backendfactories)) 97 + for i, factory := range backendfactories { 98 + backend, err := factory() 146 99 if err != nil { 147 - return nil, fmt.Errorf("failed to create Publix backend: %w", err) 100 + if locationtypes.IsDisabledBackendError(err) { 101 + continue 102 + } 103 + return nil, fmt.Errorf("failed to initialize location backend %d: %w", i, err) 148 104 } 149 - backends = append(backends, publixBackend) 105 + backends = append(backends, backend) 150 106 } 151 - if cfg.HEB.IsEnabled() { 152 - slog.Info("initializing HEB location backend") 153 - listCache, err := cache.EnsureCache(heb.Container) 154 - if err != nil { 155 - return nil, fmt.Errorf("failed to create HEB list cache: %w", err) 156 - } 157 107 158 - hebBackend, err := heb.NewLocationBackend(context.Background(), listCache, centroids) 159 - if err != nil { 160 - return nil, fmt.Errorf("failed to create HEB backend: %w", err) 161 - } 162 - backends = append(backends, hebBackend) 163 - } 164 108 return &locationStorage{ 165 109 client: backends, 166 110 zipCentroids: centroids,
+15
internal/locations/types/errors.go
··· 1 + package types 2 + 3 + import ( 4 + "errors" 5 + "fmt" 6 + ) 7 + 8 + var errDisabledBackend = errors.New("location backend is disabled") 9 + 10 + func DisabledBackendError(backend string) error { 11 + return fmt.Errorf("%w: %s", errDisabledBackend, backend) 12 + } 13 + func IsDisabledBackendError(err error) bool { 14 + return errors.Is(err, errDisabledBackend) 15 + }
+20
internal/locations/types/errors_test.go
··· 1 + package types 2 + 3 + import ( 4 + "errors" 5 + "testing" 6 + ) 7 + 8 + func TestIsDisabledBackendError(t *testing.T) { 9 + err := DisabledBackendError("HEB") 10 + 11 + if !IsDisabledBackendError(err) { 12 + t.Fatalf("expected disabled backend error to be recognized") 13 + } 14 + if !IsDisabledBackendError(errors.Join(errors.New("wrapped"), err)) { 15 + t.Fatalf("expected wrapped disabled backend error to be recognized") 16 + } 17 + if IsDisabledBackendError(errors.New("other")) { 18 + t.Fatalf("expected unrelated error to be ignored") 19 + } 20 + }
+15 -3
internal/publix/locations.go
··· 2 2 3 3 import ( 4 4 "careme/internal/cache" 5 + "careme/internal/config" 5 6 "careme/internal/locations/nearby" 6 7 locationtypes "careme/internal/locations/types" 7 8 "context" ··· 18 19 byID map[string]locationtypes.Location 19 20 } 20 21 21 - func NewLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 22 - if c == nil { 23 - return nil, fmt.Errorf("list cache is required") 22 + func NewLocationBackendFromConfig(ctx context.Context, cfg *config.Config, zipLookup centroidByZip) (*LocationBackend, error) { 23 + if !cfg.Publix.IsEnabled() { 24 + return nil, locationtypes.DisabledBackendError("Publix") 24 25 } 26 + 25 27 if zipLookup == nil { 26 28 return nil, fmt.Errorf("zip centroid lookup is required") 27 29 } 30 + 31 + listCache, err := cache.EnsureCache(Container) 32 + if err != nil { 33 + return nil, fmt.Errorf("create Publix list cache: %w", err) 34 + } 35 + 36 + return newLocationBackend(ctx, listCache, zipLookup) 37 + } 38 + 39 + func newLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 28 40 29 41 summaries, err := loadCachedStoreSummaries(ctx, c) 30 42 if err != nil {
+6 -6
internal/publix/locations_test.go
··· 16 16 t.Fatalf("CacheStoreSummary returned error: %v", err) 17 17 } 18 18 19 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 19 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 20 20 "35401": {Lat: 33.2091, Lon: -87.5692}, 21 21 }) 22 22 if err != nil { 23 - t.Fatalf("NewLocationBackend returned error: %v", err) 23 + t.Fatalf("newLocationBackend returned error: %v", err) 24 24 } 25 25 26 26 if !backend.IsID("publix_1083") { ··· 47 47 t.Fatalf("cache far summary: %v", err) 48 48 } 49 49 50 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 50 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 51 51 "35401": {Lat: 33.2091, Lon: -87.5692}, 52 52 }) 53 53 if err != nil { 54 - t.Fatalf("NewLocationBackend returned error: %v", err) 54 + t.Fatalf("newLocationBackend returned error: %v", err) 55 55 } 56 56 57 57 locs, err := backend.GetLocationsByZip(context.Background(), "35401") ··· 71 71 72 72 cacheStore := cache.NewInMemoryCache() 73 73 74 - _, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 74 + _, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 75 75 if err == nil { 76 - t.Fatal("expected NewLocationBackend to return an error") 76 + t.Fatal("expected newLocationBackend to return an error") 77 77 } 78 78 if !strings.Contains(err.Error(), "failed to load publix locations") { 79 79 t.Fatalf("expected missing summaries error, got %v", err)
+8 -1
internal/walmart/client.go
··· 2 2 3 3 import ( 4 4 "bytes" 5 - "careme/internal/config" 6 5 "context" 7 6 "crypto" 8 7 "crypto/rand" ··· 19 18 "sort" 20 19 "strings" 21 20 "time" 21 + 22 + "careme/internal/config" 23 + locationtypes "careme/internal/locations/types" 22 24 23 25 "golang.org/x/crypto/ssh" 24 26 ) ··· 49 51 50 52 // NewClient creates a Walmart affiliates client. 51 53 func NewClient(cfg config.WalmartConfig) (*Client, error) { 54 + 55 + if !cfg.IsEnabled() { 56 + return nil, locationtypes.DisabledBackendError("Walmart") 57 + } 58 + 52 59 if strings.TrimSpace(cfg.ConsumerID) == "" { 53 60 return nil, errors.New("consumer ID is required") 54 61 }
+15 -3
internal/wholefoods/locations.go
··· 2 2 3 3 import ( 4 4 "careme/internal/cache" 5 + "careme/internal/config" 5 6 "careme/internal/locations/nearby" 6 7 locationtypes "careme/internal/locations/types" 7 8 "context" ··· 18 19 byID map[string]locationtypes.Location 19 20 } 20 21 21 - func NewLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 22 - if c == nil { 23 - return nil, fmt.Errorf("list cache is required") 22 + func NewLocationBackendFromConfig(ctx context.Context, cfg *config.Config, zipLookup centroidByZip) (*LocationBackend, error) { 23 + if !cfg.WholeFoods.IsEnabled() { 24 + return nil, locationtypes.DisabledBackendError("Whole Foods") 24 25 } 26 + 25 27 if zipLookup == nil { 26 28 return nil, fmt.Errorf("zip centroid lookup is required") 27 29 } 30 + 31 + listCache, err := cache.EnsureCache(Container) 32 + if err != nil { 33 + return nil, fmt.Errorf("create Whole Foods list cache: %w", err) 34 + } 35 + 36 + return newLocationBackend(ctx, listCache, zipLookup) 37 + } 38 + 39 + func newLocationBackend(ctx context.Context, c cache.ListCache, zipLookup centroidByZip) (*LocationBackend, error) { 28 40 29 41 //Is this too much? should we just fetch a single blob that is all coordinates -> store ids and lazily fetch stores? 30 42 summaries, err := loadCachedStoreSummaries(ctx, c)
+8 -8
internal/wholefoods/locations_test.go
··· 16 16 t.Fatalf("CacheStoreSummary returned error: %v", err) 17 17 } 18 18 19 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 19 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 20 20 "98101": {Lat: 47.6101, Lon: -122.3344}, 21 21 }) 22 22 if err != nil { 23 - t.Fatalf("NewLocationBackend returned error: %v", err) 23 + t.Fatalf("newLocationBackend returned error: %v", err) 24 24 } 25 25 26 26 if !backend.IsID("wholefoods_10216") { ··· 47 47 t.Fatalf("cache far store summary: %v", err) 48 48 } 49 49 50 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 50 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{ 51 51 "98101": {Lat: 47.6101, Lon: -122.3344}, 52 52 }) 53 53 if err != nil { 54 - t.Fatalf("NewLocationBackend returned error: %v", err) 54 + t.Fatalf("newLocationBackend returned error: %v", err) 55 55 } 56 56 57 57 locs, err := backend.GetLocationsByZip(context.Background(), "98101") ··· 80 80 t.Fatalf("cache far store summary: %v", err) 81 81 } 82 82 83 - backend, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 83 + backend, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 84 84 if err != nil { 85 - t.Fatalf("NewLocationBackend returned error: %v", err) 85 + t.Fatalf("newLocationBackend returned error: %v", err) 86 86 } 87 87 88 88 locs, err := backend.GetLocationsByZip(context.Background(), "unknown") ··· 99 99 100 100 cacheStore := cache.NewInMemoryCache() 101 101 102 - _, err := NewLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 102 + _, err := newLocationBackend(context.Background(), cacheStore, staticZIPLookup{}) 103 103 if err == nil { 104 - t.Fatal("expected NewLocationBackend to return an error") 104 + t.Fatal("expected newLocationBackend to return an error") 105 105 } 106 106 if !strings.Contains(err.Error(), "failed to load wholefoods locations") { 107 107 t.Fatalf("expected missing summaries error, got %v", err)