Skip to content

Commit 9572077

Browse files
[CLD-781]: fix(datstore): change datastore type to enum (#534)
Instead of using string , we use enum to represent the location of the datastore data. Also exposes the location in the field of the Config struct so it can be used to select the right client to load. JIRA: https://smartcontract-it.atlassian.net/browse/CLD-781
1 parent 9772765 commit 9572077

7 files changed

Lines changed: 181 additions & 30 deletions

File tree

.changeset/good-chairs-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"chainlink-deployments-framework": patch
3+
---
4+
5+
fix(catalog): use enum instead of string

engine/cld/config/config.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger"
88

9+
cfgdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/domain"
910
cfgenv "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/env"
1011
cfgjira "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/jira"
1112
cfgnet "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/network"
@@ -29,6 +30,9 @@ type Config struct {
2930
// Jira contains JIRA integration configuration including connection details
3031
// and field mappings for using JIRA in resolvers.
3132
Jira *cfgjira.Config
33+
34+
// DatastoreType specifies the type of datastore to use (either "file" or "catalog").
35+
DatastoreType cfgdomain.DatastoreType
3236
}
3337

3438
// Load loads and consolidates all configuration required for a domain environment, including
@@ -54,9 +58,15 @@ func Load(dom fdomain.Domain, env string, lggr logger.Logger) (*Config, error) {
5458
}
5559
}
5660

61+
datastoreType, err := LoadDatastoreType(dom, env)
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to load datastore type config: %w", err)
64+
}
65+
5766
return &Config{
58-
Networks: networks,
59-
Env: envCfg,
60-
Jira: jiraCfg,
67+
Networks: networks,
68+
Env: envCfg,
69+
Jira: jiraCfg,
70+
DatastoreType: datastoreType,
6171
}, nil
6272
}

engine/cld/config/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ func Test_Load(t *testing.T) {
7171
require.NoError(t, err)
7272
require.NotEmpty(t, got.Networks)
7373
require.NotNil(t, got.Env)
74+
require.NotEmpty(t, got.DatastoreType, "DatastoreType should be loaded")
7475
}
7576
})
7677
}

engine/cld/config/domain/domain.go

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,30 @@ import (
99
"github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/jira"
1010
)
1111

12+
// DatastoreType represents the type of datastore to use for persisting deployment data.
13+
type DatastoreType string
14+
15+
const (
16+
// DatastoreTypeFile indicates data should be persisted to local JSON files (default behavior).
17+
DatastoreTypeFile DatastoreType = "file"
18+
// DatastoreTypeCatalog indicates data should be persisted to the remote catalog service.
19+
DatastoreTypeCatalog DatastoreType = "catalog"
20+
)
21+
22+
// String returns the string representation of the DatastoreType.
23+
func (d DatastoreType) String() string {
24+
return string(d)
25+
}
26+
27+
// IsValid checks if the DatastoreType is a valid value.
28+
func (d DatastoreType) IsValid() bool {
29+
return d == DatastoreTypeFile || d == DatastoreTypeCatalog
30+
}
31+
1232
// Environment represents a single environment configuration.
1333
type Environment struct {
14-
NetworkTypes []string `mapstructure:"network_types" yaml:"network_types"`
15-
Datastore string `mapstructure:"datastore" yaml:"datastore"`
34+
NetworkTypes []string `mapstructure:"network_types" yaml:"network_types"`
35+
Datastore DatastoreType `mapstructure:"datastore" yaml:"datastore"`
1636
}
1737

1838
// validate validates the environment configuration.
@@ -38,8 +58,8 @@ func (e *Environment) validate() error {
3858
}
3959

4060
// Validate datastore field if provided
41-
if e.Datastore != "" && !isValidDatastore(e.Datastore) {
42-
return errors.New("invalid datastore value: " + e.Datastore + " (must be 'file' or 'catalog')")
61+
if e.Datastore != "" && !e.Datastore.IsValid() {
62+
return fmt.Errorf("invalid datastore value: %s (must be 'file' or 'catalog')", e.Datastore)
4363
}
4464

4565
return nil
@@ -50,11 +70,6 @@ func isValidNetworkType(networkType string) bool {
5070
return networkType == "mainnet" || networkType == "testnet"
5171
}
5272

53-
// isValidDatastore checks if the datastore value is valid.
54-
func isValidDatastore(datastore string) bool {
55-
return datastore == "file" || datastore == "catalog"
56-
}
57-
5873
// DomainConfig represents the parsed and validated domain configuration.
5974
type DomainConfig struct {
6075
Environments map[string]Environment `mapstructure:"environments" yaml:"environments"`
@@ -101,7 +116,7 @@ func Load(filePath string) (*DomainConfig, error) {
101116
// Apply defaults to environments
102117
for name, env := range cfg.Environments {
103118
if env.Datastore == "" {
104-
env.Datastore = "file" // Default to file if not specified
119+
env.Datastore = DatastoreTypeFile // Default to file if not specified
105120
cfg.Environments[name] = env
106121
}
107122
}

engine/cld/config/domain/domain_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,32 +48,32 @@ func TestIsValidNetworkType(t *testing.T) {
4848
}
4949
}
5050

51-
func TestIsValidDatastore(t *testing.T) {
51+
func TestDatastoreType_IsValid(t *testing.T) {
5252
t.Parallel()
5353

5454
tests := []struct {
5555
name string
56-
datastore string
56+
datastore DatastoreType
5757
expected bool
5858
}{
5959
{
6060
name: "file is valid",
61-
datastore: "file",
61+
datastore: DatastoreTypeFile,
6262
expected: true,
6363
},
6464
{
6565
name: "catalog is valid",
66-
datastore: "catalog",
66+
datastore: DatastoreTypeCatalog,
6767
expected: true,
6868
},
6969
{
7070
name: "invalid value",
71-
datastore: "invalid",
71+
datastore: DatastoreType("invalid"),
7272
expected: false,
7373
},
7474
{
7575
name: "empty value",
76-
datastore: "",
76+
datastore: DatastoreType(""),
7777
expected: false,
7878
},
7979
}
@@ -82,7 +82,7 @@ func TestIsValidDatastore(t *testing.T) {
8282
t.Run(tt.name, func(t *testing.T) {
8383
t.Parallel()
8484

85-
assert.Equal(t, tt.expected, isValidDatastore(tt.datastore))
85+
assert.Equal(t, tt.expected, tt.datastore.IsValid())
8686
})
8787
}
8888
}
@@ -121,15 +121,15 @@ func TestEnvironment_Validate(t *testing.T) {
121121
name: "valid environment with file datastore",
122122
environment: Environment{
123123
NetworkTypes: []string{"testnet"},
124-
Datastore: "file",
124+
Datastore: DatastoreTypeFile,
125125
},
126126
wantErr: false,
127127
},
128128
{
129129
name: "valid environment with catalog datastore",
130130
environment: Environment{
131131
NetworkTypes: []string{"mainnet"},
132-
Datastore: "catalog",
132+
Datastore: DatastoreTypeCatalog,
133133
},
134134
wantErr: false,
135135
},
@@ -177,7 +177,7 @@ func TestEnvironment_Validate(t *testing.T) {
177177
name: "invalid datastore value",
178178
environment: Environment{
179179
NetworkTypes: []string{"testnet"},
180-
Datastore: "invalid",
180+
Datastore: DatastoreType("invalid"),
181181
},
182182
wantErr: true,
183183
errContains: "invalid datastore value: invalid",
@@ -231,27 +231,27 @@ func TestLoad(t *testing.T) {
231231
// Test specific environment configurations
232232
dev := config.Environments["development"]
233233
assert.Equal(t, []string{"testnet"}, dev.NetworkTypes)
234-
assert.Equal(t, "file", dev.Datastore)
234+
assert.Equal(t, DatastoreTypeFile, dev.Datastore)
235235

236236
staging := config.Environments["staging"]
237237
assert.ElementsMatch(t, []string{"testnet", "mainnet"}, staging.NetworkTypes)
238-
assert.Equal(t, "catalog", staging.Datastore)
238+
assert.Equal(t, DatastoreTypeCatalog, staging.Datastore)
239239

240240
prod := config.Environments["production"]
241241
assert.Equal(t, []string{"mainnet"}, prod.NetworkTypes)
242-
assert.Equal(t, "catalog", prod.Datastore)
242+
assert.Equal(t, DatastoreTypeCatalog, prod.Datastore)
243243

244244
local := config.Environments["local"]
245245
assert.Equal(t, []string{"testnet"}, local.NetworkTypes)
246-
assert.Equal(t, "file", local.Datastore)
246+
assert.Equal(t, DatastoreTypeFile, local.Datastore)
247247

248248
dudeenv := config.Environments["dudeenv"]
249249
assert.Equal(t, []string{"testnet"}, dudeenv.NetworkTypes)
250-
assert.Equal(t, "file", dudeenv.Datastore) // Not set in YAML, should default to "file"
250+
assert.Equal(t, DatastoreTypeFile, dudeenv.Datastore) // Not set in YAML, should default to "file"
251251

252252
testtest := config.Environments["testtest"]
253253
assert.Equal(t, []string{"mainnet"}, testtest.NetworkTypes)
254-
assert.Equal(t, "catalog", testtest.Datastore)
254+
assert.Equal(t, DatastoreTypeCatalog, testtest.Datastore)
255255
},
256256
},
257257
{
@@ -283,7 +283,7 @@ func TestLoad(t *testing.T) {
283283

284284
// dudeenv doesn't have datastore specified in YAML, should default to "file"
285285
dudeenv := config.Environments["dudeenv"]
286-
assert.Equal(t, "file", dudeenv.Datastore)
286+
assert.Equal(t, DatastoreTypeFile, dudeenv.Datastore)
287287
},
288288
},
289289
}

engine/cld/config/env.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,28 @@ import (
44
"fmt"
55
"path/filepath"
66

7+
cfgdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/domain"
78
cfgenv "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/env"
89
fdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
910
)
1011

12+
// LoadDatastoreType retrieves the datastore type configuration for a given domain and environment.
13+
// It loads the domain configuration file and extracts the datastore setting for the specified environment.
14+
func LoadDatastoreType(dom fdomain.Domain, env string) (cfgdomain.DatastoreType, error) {
15+
domainCfgPath := dom.ConfigDomainFilePath()
16+
domainCfg, err := cfgdomain.Load(domainCfgPath)
17+
if err != nil {
18+
return "", fmt.Errorf("failed to load domain config: %w", err)
19+
}
20+
21+
envConfig, exists := domainCfg.Environments[env]
22+
if !exists {
23+
return "", fmt.Errorf("environment %s not found in domain config", env)
24+
}
25+
26+
return envConfig.Datastore, nil
27+
}
28+
1129
// LoadEnvConfig retrieves the environment configuration for a given domain and environment.
1230
//
1331
// Loading strategy:

engine/cld/config/env_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/stretchr/testify/require"
99

1010
cfgenv "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/config/env"
11+
fdomain "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/domain"
1112
)
1213

1314
func Test_LoadEnvConfig(t *testing.T) { //nolint:paralleltest // These tests are not parallel safe due to setting of env vars
@@ -192,3 +193,104 @@ func Test_LoadEnvConfig(t *testing.T) { //nolint:paralleltest // These tests are
192193
})
193194
}
194195
}
196+
197+
func Test_LoadDatastoreType(t *testing.T) {
198+
t.Parallel()
199+
200+
tests := []struct {
201+
name string
202+
beforeFunc func(t *testing.T, dom fdomain.Domain, envKey string)
203+
wantValue string
204+
wantErr string
205+
}{
206+
{
207+
name: "successfully loads datastore type - file",
208+
beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) {
209+
t.Helper()
210+
211+
writeConfigDomainFile(t, dom, "domain.yaml")
212+
},
213+
wantValue: "file",
214+
},
215+
{
216+
name: "successfully loads datastore type - catalog",
217+
beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) {
218+
t.Helper()
219+
220+
// Create a custom domain.yaml with catalog datastore
221+
domainYAML := `environments:
222+
staging_testnet:
223+
network_types:
224+
- testnet
225+
datastore: catalog
226+
`
227+
err := os.WriteFile(dom.ConfigDomainFilePath(), []byte(domainYAML), filePerms)
228+
require.NoError(t, err)
229+
},
230+
wantValue: "catalog",
231+
},
232+
{
233+
name: "successfully loads datastore type - defaults to file when not specified",
234+
beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) {
235+
t.Helper()
236+
237+
// Create a domain.yaml without datastore field
238+
domainYAML := `environments:
239+
staging_testnet:
240+
network_types:
241+
- testnet
242+
`
243+
err := os.WriteFile(dom.ConfigDomainFilePath(), []byte(domainYAML), filePerms)
244+
require.NoError(t, err)
245+
},
246+
wantValue: "file",
247+
},
248+
{
249+
name: "fails when domain config file does not exist",
250+
beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) {
251+
t.Helper()
252+
// Don't create domain.yaml file
253+
},
254+
wantErr: "failed to load domain config",
255+
},
256+
{
257+
name: "fails when environment not found in domain config",
258+
beforeFunc: func(t *testing.T, dom fdomain.Domain, envKey string) {
259+
t.Helper()
260+
261+
// Create a domain.yaml with a different environment
262+
domainYAML := `environments:
263+
production:
264+
network_types:
265+
- mainnet
266+
datastore: file
267+
`
268+
err := os.WriteFile(dom.ConfigDomainFilePath(), []byte(domainYAML), filePerms)
269+
require.NoError(t, err)
270+
},
271+
wantErr: "environment staging_testnet not found in domain config",
272+
},
273+
}
274+
275+
for _, tt := range tests {
276+
t.Run(tt.name, func(t *testing.T) {
277+
t.Parallel()
278+
279+
dom, envKey := setupConfigDirs(t)
280+
281+
if tt.beforeFunc != nil {
282+
tt.beforeFunc(t, dom, envKey)
283+
}
284+
285+
got, err := LoadDatastoreType(dom, envKey)
286+
287+
if tt.wantErr != "" {
288+
require.Error(t, err)
289+
require.ErrorContains(t, err, tt.wantErr)
290+
} else {
291+
require.NoError(t, err)
292+
assert.Equal(t, tt.wantValue, got.String())
293+
}
294+
})
295+
}
296+
}

0 commit comments

Comments
 (0)