Skip to content

Commit 99a5764

Browse files
committed
refactor: use typed struct for AgentDefinition parsing
1 parent 1c4340e commit 99a5764

4 files changed

Lines changed: 34 additions & 24 deletions

File tree

cmd/agent-metadata-action/main.go

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,16 +243,8 @@ func runAgentFlow(ctx context.Context, client metadataClient, workspace, agentTy
243243
AgentControlDefinitions: agentControl,
244244
}
245245
if agentDef != nil {
246-
if b, ok := (*agentDef)["bindings"]; ok {
247-
if bindings, ok := b.([]interface{}); ok {
248-
metadata.Bindings = bindings
249-
}
250-
}
251-
if bc, ok := (*agentDef)["breakingChange"]; ok {
252-
if breakingChange, ok := bc.(string); ok {
253-
metadata.BreakingChange = &breakingChange
254-
}
255-
}
246+
metadata.Bindings = agentDef.Bindings
247+
metadata.BreakingChange = agentDef.BreakingChange
256248
}
257249

258250
printJSON(ctx, "Agent Metadata", metadata)

internal/loader/definitions.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,10 @@ func ReadAgentDefinition(ctx context.Context, workspacePath string) (*models.Age
117117
return nil, fmt.Errorf("failed to read agentDefinition.yml: %w", err)
118118
}
119119

120-
var content map[string]interface{}
121-
if err := yaml.Unmarshal(data, &content); err != nil {
120+
var def models.AgentDefinition
121+
if err := yaml.Unmarshal(data, &def); err != nil {
122122
return nil, fmt.Errorf("failed to parse agentDefinition.yml: %w", err)
123123
}
124-
125-
def := models.AgentDefinition(content)
126124
return &def, nil
127125
}
128126

internal/loader/definitions_test.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,13 +1032,11 @@ breakingChange: "2.0.0"`
10321032
require.NoError(t, err)
10331033
require.NotNil(t, result)
10341034

1035-
assert.Equal(t, "2.0.0", (*result)["breakingChange"])
1035+
require.NotNil(t, result.BreakingChange)
1036+
assert.Equal(t, "2.0.0", *result.BreakingChange)
10361037

1037-
bindings, ok := (*result)["bindings"].([]interface{})
1038-
require.True(t, ok, "bindings should be a slice")
1039-
require.Len(t, bindings, 1)
1040-
1041-
binding := bindings[0].(map[string]interface{})
1038+
require.Len(t, result.Bindings, 1)
1039+
binding := result.Bindings[0].(map[string]interface{})
10421040
assert.Equal(t, "REQUIRES", binding["type"])
10431041
assert.Equal(t, "AGENT", binding["targetType"])
10441042
assert.Equal(t, "NrInfra", binding["target"])
@@ -1056,7 +1054,26 @@ func TestReadAgentDefinition_BreakingChangeOnly(t *testing.T) {
10561054
result, err := ReadAgentDefinition(context.Background(), tmpDir)
10571055
require.NoError(t, err)
10581056
require.NotNil(t, result)
1059-
assert.Equal(t, "1.5.0", (*result)["breakingChange"])
1057+
require.NotNil(t, result.BreakingChange)
1058+
assert.Equal(t, "1.5.0", *result.BreakingChange)
1059+
assert.Nil(t, result.Bindings)
1060+
}
1061+
1062+
func TestReadAgentDefinition_BreakingChangeUnquotedFloat(t *testing.T) {
1063+
tmpDir := t.TempDir()
1064+
configDir := filepath.Join(tmpDir, config.GetRootFolderForAgentRepo())
1065+
require.NoError(t, os.MkdirAll(configDir, 0755))
1066+
1067+
// Unquoted float — go-yaml coerces to string representation
1068+
yamlContent := `breakingChange: 2.0`
1069+
defFile := filepath.Join(configDir, "agentDefinition.yml")
1070+
require.NoError(t, os.WriteFile(defFile, []byte(yamlContent), 0644))
1071+
1072+
result, err := ReadAgentDefinition(context.Background(), tmpDir)
1073+
require.NoError(t, err)
1074+
require.NotNil(t, result)
1075+
require.NotNil(t, result.BreakingChange)
1076+
assert.Equal(t, "2.0", *result.BreakingChange)
10601077
}
10611078

10621079
func TestReadAgentDefinition_InvalidYAML(t *testing.T) {

internal/models/models.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ type Metadata map[string]interface{}
2424
// YAML fields are automatically translated to JSON.
2525
type AgentControlDefinition map[string]interface{}
2626

27-
// AgentDefinition represents agent-level bindings and constraints declared in agentDefinition.yml.
28-
// It uses a map to allow any attributes (bindings, breakingChange, etc.) to pass through without code changes.
29-
type AgentDefinition map[string]interface{}
27+
// AgentDefinition is a typed view of agentDefinition.yml used for parsing.
28+
// Fields are promoted flat onto AgentMetadata for JSON serialization.
29+
type AgentDefinition struct {
30+
Bindings []interface{} `yaml:"bindings"`
31+
BreakingChange *string `yaml:"breakingChange"`
32+
}
3033

3134
// ConfigFile represents the YAML file structure containing multiple configs
3235
type ConfigFile struct {

0 commit comments

Comments
 (0)