Skip to content

Commit 6d3ae91

Browse files
committed
introduce product-aware config
1 parent 8e4de79 commit 6d3ae91

File tree

1 file changed

+53
-10
lines changed

1 file changed

+53
-10
lines changed

framework/tracking/dx.go

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ type DxTracker struct {
6161
// NewDxTracker initializes a tracker with automatic GitHub CLI integration for authentication.
6262
// APITokenVariableName is the name of the GitHub repository variable containing the DX API token.
6363
// Each DX project has its own token for tracking purposes.
64-
func NewDxTracker(APITokenVariableName string) (Tracker, error) {
64+
func NewDxTracker(APITokenVariableName, product string) (Tracker, error) {
6565
t := &DxTracker{}
6666

6767
lvlStr := os.Getenv(EnvVarLogLevel)
@@ -86,21 +86,30 @@ func NewDxTracker(APITokenVariableName string) (Tracker, error) {
8686
t.logger.Debug().Msg("Tracking in test mode")
8787
}
8888

89+
if APITokenVariableName == "" {
90+
return nil, errors.New("API token variable name is required. It is the name of the GitHub repository variable containing the DX API token")
91+
}
92+
93+
if product == "" {
94+
return nil, errors.New("product is required. Each DX project has its own token for tracking purposes")
95+
}
96+
product = strings.ToLower(product)
97+
8998
c, isConfigAvailable, configErr := openConfig()
9099
if configErr != nil {
91100
return nil, errors.Wrap(configErr, "failed to open local config")
92101
}
93102

94103
// if local config is available read it and set mode to online
95-
if isConfigAvailable && isConfigValid(c) {
104+
if isConfigAvailable && isConfigValid(c, product) {
96105
t.logger.Debug().Msg("Valid local config found")
97106
t.mode = ModeOnline
98107
} else {
99108
// if local config is not available check if GH CLI is available
100109
// and if so, try to configure tracker with it
101110
if t.checkIfGhCLIAvailable() {
102111
var configErr error
103-
c, configErr = t.buildConfigWithGhCLI(APITokenVariableName)
112+
c, configErr = t.buildConfigWithGhCLI(APITokenVariableName, product)
104113
if configErr != nil {
105114
t.mode = ModeOffline
106115
t.logger.Warn().Msgf("Failed to build config with GH CLI: %s", configErr.Error())
@@ -121,7 +130,8 @@ func NewDxTracker(APITokenVariableName string) (Tracker, error) {
121130
}
122131

123132
if t.mode == ModeOnline {
124-
t.apiToken = c.DxAPIToken
133+
// at this point config should be available and valid, since we've checked it above
134+
t.apiToken = c.APITokens[product]
125135
t.githubUsername = c.GithubUsername
126136

127137
go func() {
@@ -137,19 +147,21 @@ func NewDxTracker(APITokenVariableName string) (Tracker, error) {
137147
return t, nil
138148
}
139149

140-
func (t *DxTracker) buildConfigWithGhCLI(APITokenVariableName string) (*config, error) {
150+
func (t *DxTracker) buildConfigWithGhCLI(APITokenVariableName, product string) (*config, error) {
141151
var userNameErr error
142152
c := &config{}
143153
c.GithubUsername, userNameErr = t.readGHUsername()
144154
if userNameErr != nil {
145155
return nil, errors.Wrap(userNameErr, "failed to read github username")
146156
}
147157

148-
var apiTokenErr error
149-
c.DxAPIToken, apiTokenErr = t.readDXAPIToken(APITokenVariableName)
158+
apiToken, apiTokenErr := t.readDXAPIToken(APITokenVariableName)
150159
if apiTokenErr != nil {
151160
return nil, errors.Wrap(apiTokenErr, "failed to read DX API token")
152161
}
162+
c.APITokens = map[Product]string{
163+
product: apiToken,
164+
}
153165

154166
saveErr := saveConfig(c)
155167
if saveErr != nil {
@@ -322,12 +334,20 @@ func (t *DxTracker) readDXAPIToken(APITokenVariableName string) (string, error)
322334
}
323335

324336
// config stores authentication credentials for the DX API.
325-
type config struct {
337+
// deprecated: legacyConfig is used to read old config files and migrate them to the new format. Use config instead.
338+
type legacyConfig struct {
326339
DxAPIToken string `json:"dx_api_token"`
327340
GithubUsername string `json:"github_username"`
328341
}
329342

343+
type Product = string
344+
type config struct {
345+
GithubUsername string `json:"github_username"`
346+
APITokens map[Product]string `json:"tokens"`
347+
}
348+
330349
// openConfig attempts to load existing configuration from the user's home directory.
350+
// If a legacy config is found, it is migrated to the new format.
331351
func openConfig() (*config, bool, error) {
332352
configPath, pathErr := configPath()
333353
if pathErr != nil {
@@ -343,6 +363,29 @@ func openConfig() (*config, bool, error) {
343363
return nil, false, errors.Wrap(readErr, "failed to read config file")
344364
}
345365

366+
var legacyConfig legacyConfig
367+
legacyUnmarshalErr := json.Unmarshal(configContent, &legacyConfig)
368+
if legacyUnmarshalErr != nil {
369+
return nil, false, errors.Wrap(legacyUnmarshalErr, "failed to unmarshal legacy config file")
370+
}
371+
372+
if legacyConfig.DxAPIToken != "" && legacyConfig.GithubUsername != "" {
373+
newConfig := config{
374+
GithubUsername: legacyConfig.GithubUsername,
375+
APITokens: map[Product]string{
376+
// it is the only product that uses tracking at the moment
377+
"local_CRE": legacyConfig.DxAPIToken,
378+
},
379+
}
380+
381+
saveErr := saveConfig(&newConfig)
382+
if saveErr != nil {
383+
return nil, false, errors.Wrap(saveErr, "failed to save new config")
384+
}
385+
386+
return &newConfig, true, nil
387+
}
388+
346389
var localConfig config
347390
unmarshalErr := json.Unmarshal(configContent, &localConfig)
348391
if unmarshalErr != nil {
@@ -353,8 +396,8 @@ func openConfig() (*config, bool, error) {
353396
}
354397

355398
// isConfigValid ensures both API token and GitHub username are present.
356-
func isConfigValid(c *config) bool {
357-
return c.DxAPIToken != "" && c.GithubUsername != ""
399+
func isConfigValid(c *config, product string) bool {
400+
return c.APITokens[product] != "" && c.GithubUsername != ""
358401
}
359402

360403
// saveConfig persists configuration to the user's home directory with proper permissions.

0 commit comments

Comments
 (0)