Skip to content

Commit

Permalink
Allow unsetting globals
Browse files Browse the repository at this point in the history
  • Loading branch information
ahobsonsayers committed Sep 25, 2024
1 parent 4b4654d commit 982901e
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 84 deletions.
108 changes: 69 additions & 39 deletions cmd/twitchets/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
type Config struct {
APIKey string `json:"apiKey"`
GlobalConfig GlobalEventConfig `json:"global"`
TicketsConfig []twickets.Filter `json:"tickets"`
TicketsConfig []TicketConfig `json:"tickets"`
}

func (c Config) Validate() error {
Expand All @@ -27,8 +27,8 @@ func (c Config) Validate() error {
return fmt.Errorf("global config is not valid: %w", err)
}

for idx, event := range c.TicketsConfig {
err := event.Validate()
for idx, ticketConfig := range c.TicketsConfig {
err := ticketConfig.Validate()
if err != nil {
return fmt.Errorf("event config at index [%d] is no valid: %w", idx, err)
}
Expand All @@ -37,25 +37,38 @@ func (c Config) Validate() error {
return nil
}

func (c Config) applyGlobalConfig() {
for idx, eventConfig := range c.TicketsConfig {
// Apply regions
if len(eventConfig.Regions) == 0 && len(c.GlobalConfig.Regions) != 0 {
eventConfig.Regions = c.GlobalConfig.Regions
func (c Config) Filters() []twickets.Filter {
filters := make([]twickets.Filter, 0, len(c.TicketsConfig))
for _, ticketConfig := range c.TicketsConfig {

var filter twickets.Filter
filter.Name = ticketConfig.Name

// Set regions
if ticketConfig.Regions == nil {
filter.Regions = c.GlobalConfig.Regions
} else {
filter.Regions = ticketConfig.Regions
}

// Apply num tickets
if eventConfig.NumTickets == 0 && c.GlobalConfig.NumTickets != 0 {
eventConfig.NumTickets = c.GlobalConfig.NumTickets
// Set num tickets
if ticketConfig.NumTickets == nil {
filter.NumTickets = c.GlobalConfig.NumTickets
} else if *ticketConfig.NumTickets > 0 {
filter.NumTickets = *ticketConfig.NumTickets
}

// Apply discount
if eventConfig.Discount == 0 && c.GlobalConfig.Discount != 0 {
eventConfig.Discount = c.GlobalConfig.Discount
// Set discount
if ticketConfig.Discount == nil {
filter.Discount = c.GlobalConfig.Discount
} else if *ticketConfig.Discount > 0 {
filter.Discount = *ticketConfig.Discount
}

c.TicketsConfig[idx] = eventConfig
filters = append(filters, filter)
}

return filters
}

// GlobalEventConfig is config that applies to all events,
Expand Down Expand Up @@ -91,9 +104,47 @@ func (c GlobalEventConfig) Validate() error {
return nil
}

func (c *Config) parseKoanf(k *koanf.Koanf) error {
type TicketConfig struct {
Name string `json:"name"`
Regions []twickets.Region `json:"regions"`
NumTickets *int `json:"numTickets"`
Discount *float64 `json:"discount"`
}

func (t TicketConfig) Validate() error {
if t.Name == "" {
return errors.New("event name must be set")
}

for _, region := range t.Regions {
if !twickets.Regions.Contains(region) {
return fmt.Errorf("region '%s' is not valid", region)
}
}

return nil
}

func LoadConfig(filePath string) (Config, error) {
// Load config.
k := koanf.New(".")
err := k.Load(file.Provider(filePath), yaml.Parser())
if err != nil {
return Config{}, fmt.Errorf("error loading config: %w", err)
}

// Parse config
config, err := parseKoanf(k)
if err != nil {
return Config{}, fmt.Errorf("error parsing config: %w", err)
}

return config, nil
}

func parseKoanf(k *koanf.Koanf) (Config, error) {
if k == nil {
return nil
return Config{}, nil
}

// Parse config
Expand All @@ -115,31 +166,10 @@ func (c *Config) parseKoanf(k *koanf.Koanf) error {
},
)
if err != nil {
return fmt.Errorf("failed to unmarshal config: %w", err)
return Config{}, fmt.Errorf("failed to unmarshal config: %w", err)
}

err = config.Validate()
if err != nil {
return fmt.Errorf("invalid config: %w", err)
}

config.applyGlobalConfig()

*c = config
return nil
}

func LoadConfig(filePath string) (Config, error) {
// Load config.
k := koanf.New(".")
err := k.Load(file.Provider(filePath), yaml.Parser())
if err != nil {
return Config{}, fmt.Errorf("error loading config: %w", err)
}

// Parse config
config := Config{}
err = config.parseKoanf(k)
if err != nil {
return Config{}, fmt.Errorf("invalid config: %w", err)
}
Expand Down
122 changes: 82 additions & 40 deletions cmd/twitchets/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,102 @@ import (

"github.com/ahobsonsayers/twitchets/test/testutils"
"github.com/ahobsonsayers/twitchets/twickets"
"github.com/samber/lo"
"github.com/stretchr/testify/require"
)

func TestLoadConfig(t *testing.T) {
configPath := testutils.ProjectDirectoryJoin(t, "test", "assets", "config", "config.yaml")
config, err := LoadConfig(configPath)
actualConfig, err := LoadConfig(configPath)
require.NoError(t, err)

globalCountry := twickets.CountryUnitedKingdom
globalRegions := []twickets.Region{twickets.RegionLondon, twickets.RegionNorthWest}
globalNumTickets := 2
globalDiscount := 25
globalDiscount := 25.0

expectedConfig := Config{
APIKey: "test",
GlobalConfig: GlobalEventConfig{
Country: globalCountry,
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
TicketsConfig: []TicketConfig{
{
// Event with only name set
Name: "Event 1",
},
{
// Event with regions set
Name: "Event 2",
Regions: []twickets.Region{twickets.RegionSouthWest},
},
{
// Event with num tickets set
Name: "Event 3",
NumTickets: lo.ToPtr(1),
},
{
// Event with discount set
Name: "Event 4",
Discount: lo.ToPtr(15.0),
},
},
}

require.Equal(t, globalCountry, config.GlobalConfig.Country)
require.Equal(t, globalRegions, config.GlobalConfig.Regions)
require.Equal(t, globalNumTickets, config.GlobalConfig.NumTickets)
require.InDelta(t, globalDiscount, config.GlobalConfig.Discount, 0)
require.EqualValues(t, expectedConfig, actualConfig)
}

require.Len(t, config.TicketsConfig, 4)
func TestConfigFilters(t *testing.T) {
configPath := testutils.ProjectDirectoryJoin(t, "test", "assets", "config", "config.yaml")
config, err := LoadConfig(configPath)
require.NoError(t, err)

// Event with only name set
event1 := config.TicketsConfig[0]
// Global Config
require.Equal(t, globalRegions, event1.Regions)
require.Equal(t, globalNumTickets, event1.NumTickets)
require.InDelta(t, globalDiscount, event1.Discount, 0)
// Event config
require.Equal(t, "Event 1", event1.Name)
actualFilters := config.Filters()

// Event with regions set
event2 := config.TicketsConfig[1]
// Global Config
require.Equal(t, globalNumTickets, event2.NumTickets)
require.InDelta(t, globalDiscount, event2.Discount, 0)
// Event config
require.Equal(t, "Event 2", event2.Name)
require.Len(t, event2.Regions, 1)
require.Equal(t, event2.Regions[0], twickets.RegionSouthWest)
globalRegions := []twickets.Region{twickets.RegionLondon, twickets.RegionNorthWest}
globalNumTickets := 2
globalDiscount := 25.0

// Event with num tickets set
event3 := config.TicketsConfig[2]
// Global Config
require.Equal(t, globalRegions, event3.Regions)
require.InDelta(t, globalDiscount, event3.Discount, 0)
// Event config
require.Equal(t, "Event 3", event3.Name)
require.Equal(t, 1, event3.NumTickets)
expectedFilters := []twickets.Filter{
{
// Event with only name set
Name: "Event 1",
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
{
// Event with regions set
Name: "Event 2",
Regions: []twickets.Region{twickets.RegionSouthWest},
NumTickets: globalNumTickets,
Discount: globalDiscount,
},
{
// Event with num tickets set
Name: "Event 3",
Regions: globalRegions,
NumTickets: 1,
Discount: globalDiscount,
},
{
// Event with discount set
Name: "Event 4",
Regions: globalRegions,
NumTickets: globalNumTickets,
Discount: 15.0,
},
{
// Event with globals unset
Name: "Event 5",
Regions: []twickets.Region{},
NumTickets: 0,
Discount: 0.0,
},
}

// Event with discount set
event4 := config.TicketsConfig[3]
// Global Config
require.Equal(t, globalRegions, event4.Regions)
require.Equal(t, globalNumTickets, event4.NumTickets)
// Event config
require.Equal(t, "Event 4", event4.Name)
require.InDelta(t, 15.0, event4.Discount, 0) // nolint: testifylint
require.EqualValues(t, expectedFilters, actualFilters)
}
2 changes: 1 addition & 1 deletion cmd/twitchets/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func fetchAndProcessTickets(
slog.Warn("Fetched the max number of tickets allowed. It is possible tickets have been missed.")
}

filteredTickets := tickets.Filter(config.TicketsConfig)
filteredTickets := tickets.Filter(config.Filters())
for _, ticket := range filteredTickets {
slog.Info(
"Found tickets for monitored event",
Expand Down
4 changes: 4 additions & 0 deletions test/assets/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ tickets:
numTickets: 1
- name: Event 4
discount: 15
- name: Event 5
regions: []
numTickets: -1
discount: -1
8 changes: 4 additions & 4 deletions twickets/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
)

type Filter struct {
Name string `json:"name"`
Regions []Region `json:"regions"`
NumTickets int `json:"numTickets"`
Discount float64 `json:"discount"`
Name string
Regions []Region
NumTickets int
Discount float64
}

func (f Filter) Validate() error {
Expand Down

0 comments on commit 982901e

Please sign in to comment.