Skip to content

Commit 1c45335

Browse files
authored
Multi-value labels (#68)
* Support multi-value labels
1 parent 1ade6c2 commit 1c45335

9 files changed

Lines changed: 94 additions & 73 deletions

File tree

apptrust/commands/application/application_utils.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ func populateApplicationFromFlags(ctx *components.Context, descriptor *model.App
4444
}
4545

4646
if ctx.IsFlagSet(commands.LabelsFlag) {
47-
labelsMap, err := utils.ParseMapFlag(ctx.GetStringFlagValue(commands.LabelsFlag))
47+
labels, err := utils.ParseLabelKeyValuePairs(ctx.GetStringFlagValue(commands.LabelsFlag))
4848
if err != nil {
4949
return fmt.Errorf("failed to parse --%s: %w", commands.LabelsFlag, err)
5050
}
51-
descriptor.Labels = &labelsMap
51+
descriptor.Labels = &labels
5252
}
5353

5454
// Only set LabelUpdates if at least one of add-labels or remove-labels flags is set

apptrust/commands/application/create_app_cmd_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ func TestCreateAppCommand_Run_Flags(t *testing.T) {
4343
Description: &description,
4444
BusinessCriticality: &businessCriticality,
4545
MaturityLevel: &maturityLevel,
46-
Labels: &map[string]string{
47-
"env": "prod",
48-
"region": "us-east",
46+
Labels: &[]model.LabelEntry{
47+
{Key: "env", Value: "prod"},
48+
{Key: "region", Value: "us-east"},
4949
},
5050
UserOwners: &[]string{"john.doe", "jane.smith"},
5151
GroupOwners: &[]string{"devops", "security"},
@@ -149,10 +149,11 @@ func TestCreateAppCommand_Run_FullSpecFile(t *testing.T) {
149149
Description: &expectedDescription,
150150
MaturityLevel: &expectedMaturityLevel,
151151
BusinessCriticality: &expectedBusinessCriticality,
152-
Labels: &map[string]string{
153-
"environment": "production",
154-
"region": "us-east-1",
155-
"team": "devops",
152+
Labels: &[]model.LabelEntry{
153+
{Key: "environment", Value: "production"},
154+
{Key: "environment", Value: "staging"},
155+
{Key: "region", Value: "us-east-1"},
156+
{Key: "team", Value: "devops"},
156157
},
157158
UserOwners: &[]string{"john.doe", "jane.smith"},
158159
GroupOwners: &[]string{"devops-team", "security-team"},
@@ -281,9 +282,9 @@ func TestCreateAppCommand_Run_SpecVars(t *testing.T) {
281282
Description: &expectedDescription,
282283
MaturityLevel: &expectedMaturityLevel,
283284
BusinessCriticality: &expectedBusinessCriticality,
284-
Labels: &map[string]string{
285-
"environment": "production",
286-
"region": "us-east-1",
285+
Labels: &[]model.LabelEntry{
286+
{Key: "environment", Value: "production"},
287+
{Key: "region", Value: "us-east-1"},
287288
},
288289
}
289290

apptrust/commands/application/testfiles/full-spec.json

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,24 @@
44
"description": "A comprehensive test application",
55
"maturity_level": "production",
66
"criticality": "high",
7-
"labels": {
8-
"environment": "production",
9-
"region": "us-east-1",
10-
"team": "devops"
11-
},
7+
"labels": [
8+
{
9+
"key": "environment",
10+
"value": "production"
11+
},
12+
{
13+
"key": "environment",
14+
"value": "staging"
15+
},
16+
{
17+
"key": "region",
18+
"value": "us-east-1"
19+
},
20+
{
21+
"key": "team",
22+
"value": "devops"
23+
}
24+
],
1225
"user_owners": [
1326
"john.doe",
1427
"jane.smith"

apptrust/commands/application/testfiles/with-vars-spec.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@
44
"description": "A test application for ${ENVIRONMENT}",
55
"maturity_level": "${MATURITY_LEVEL}",
66
"criticality": "${CRITICALITY}",
7-
"labels": {
8-
"environment": "${ENVIRONMENT}",
9-
"region": "${REGION}"
10-
}
7+
"labels": [
8+
{
9+
"key": "environment",
10+
"value": "${ENVIRONMENT}"
11+
},
12+
{
13+
"key": "region",
14+
"value": "${REGION}"
15+
}
16+
]
1117
}

apptrust/commands/application/update_app_cmd_test.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ func TestUpdateAppCommand_Run(t *testing.T) {
3131
Description: &description,
3232
MaturityLevel: &maturityLevel,
3333
BusinessCriticality: &businessCriticality,
34-
Labels: &map[string]string{
35-
"environment": "production",
36-
"region": "us-east",
34+
Labels: &[]model.LabelEntry{
35+
{Key: "environment", Value: "production"},
36+
{Key: "region", Value: "us-east"},
3737
},
3838
UserOwners: &[]string{"JohnD", "Dave Rice"},
3939
GroupOwners: &[]string{"DevOps"},
@@ -67,9 +67,9 @@ func TestUpdateAppCommand_Run_Error(t *testing.T) {
6767
Description: &description,
6868
MaturityLevel: &maturityLevel,
6969
BusinessCriticality: &businessCriticality,
70-
Labels: &map[string]string{
71-
"environment": "production",
72-
"region": "us-east",
70+
Labels: &[]model.LabelEntry{
71+
{Key: "environment", Value: "production"},
72+
{Key: "region", Value: "us-east"},
7373
},
7474
UserOwners: &[]string{"JohnD", "Dave Rice"},
7575
GroupOwners: &[]string{"DevOps"},
@@ -127,7 +127,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
127127
expectsPayload: &model.AppDescriptor{
128128
ApplicationKey: "app-key",
129129
LabelUpdates: &model.LabelUpdates{
130-
Add: []model.LabelKeyValue{
130+
Add: []model.LabelEntry{
131131
{Key: "environment", Value: "production"},
132132
},
133133
},
@@ -142,7 +142,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
142142
expectsPayload: &model.AppDescriptor{
143143
ApplicationKey: "app-key",
144144
LabelUpdates: &model.LabelUpdates{
145-
Add: []model.LabelKeyValue{
145+
Add: []model.LabelEntry{
146146
{Key: "environment", Value: "production"},
147147
{Key: "region", Value: "us-east"},
148148
},
@@ -158,7 +158,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
158158
expectsPayload: &model.AppDescriptor{
159159
ApplicationKey: "app-key",
160160
LabelUpdates: &model.LabelUpdates{
161-
Add: []model.LabelKeyValue{
161+
Add: []model.LabelEntry{
162162
{Key: "environment", Value: "production"},
163163
{Key: "environment", Value: "staging"},
164164
{Key: "region", Value: "us-east"},
@@ -175,7 +175,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
175175
expectsPayload: &model.AppDescriptor{
176176
ApplicationKey: "app-key",
177177
LabelUpdates: &model.LabelUpdates{
178-
Remove: []model.LabelKeyValue{
178+
Remove: []model.LabelEntry{
179179
{Key: "infra-version", Value: "v1.0"},
180180
},
181181
},
@@ -190,7 +190,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
190190
expectsPayload: &model.AppDescriptor{
191191
ApplicationKey: "app-key",
192192
LabelUpdates: &model.LabelUpdates{
193-
Remove: []model.LabelKeyValue{
193+
Remove: []model.LabelEntry{
194194
{Key: "infra-version", Value: "v1.0"},
195195
{Key: "region", Value: "us-west"},
196196
},
@@ -207,12 +207,12 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
207207
expectsPayload: &model.AppDescriptor{
208208
ApplicationKey: "app-key",
209209
LabelUpdates: &model.LabelUpdates{
210-
Add: []model.LabelKeyValue{
210+
Add: []model.LabelEntry{
211211
{Key: "environment", Value: "production"},
212212
{Key: "environment", Value: "staging"},
213213
{Key: "region", Value: "us-east"},
214214
},
215-
Remove: []model.LabelKeyValue{
215+
Remove: []model.LabelEntry{
216216
{Key: "infra-version", Value: "v1.0"},
217217
{Key: "region", Value: "us-west"},
218218
},
@@ -246,10 +246,10 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
246246
Description: stringPtr("Updated description"),
247247
MaturityLevel: stringPtr("production"),
248248
LabelUpdates: &model.LabelUpdates{
249-
Add: []model.LabelKeyValue{
249+
Add: []model.LabelEntry{
250250
{Key: "environment", Value: "production"},
251251
},
252-
Remove: []model.LabelKeyValue{
252+
Remove: []model.LabelEntry{
253253
{Key: "old-label", Value: "old-value"},
254254
},
255255
},
@@ -282,7 +282,7 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
282282
expectsPayload: &model.AppDescriptor{
283283
ApplicationKey: "app-key",
284284
LabelUpdates: &model.LabelUpdates{
285-
Add: []model.LabelKeyValue{
285+
Add: []model.LabelEntry{
286286
{Key: "key1", Value: "value1"},
287287
{Key: "key2", Value: "value2"},
288288
},
@@ -299,8 +299,8 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
299299
expectsPayload: &model.AppDescriptor{
300300
ApplicationKey: "app-key",
301301
LabelUpdates: &model.LabelUpdates{
302-
Add: []model.LabelKeyValue{},
303-
Remove: []model.LabelKeyValue{
302+
Add: []model.LabelEntry{},
303+
Remove: []model.LabelEntry{
304304
{Key: "key", Value: "value"},
305305
},
306306
},
@@ -316,10 +316,10 @@ func TestUpdateAppCommand_FlagsSuite(t *testing.T) {
316316
expectsPayload: &model.AppDescriptor{
317317
ApplicationKey: "app-key",
318318
LabelUpdates: &model.LabelUpdates{
319-
Add: []model.LabelKeyValue{
319+
Add: []model.LabelEntry{
320320
{Key: "key", Value: "value"},
321321
},
322-
Remove: []model.LabelKeyValue{},
322+
Remove: []model.LabelEntry{},
323323
},
324324
},
325325
},

apptrust/commands/utils/utils.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,12 @@ func ParseListPropertiesFlag(propertiesStr string) (map[string][]string, error)
168168
return result, nil
169169
}
170170

171-
func ParseLabelKeyValuePairs(flagValue string) ([]model.LabelKeyValue, error) {
171+
func ParseLabelKeyValuePairs(flagValue string) ([]model.LabelEntry, error) {
172172
if flagValue == "" {
173-
return []model.LabelKeyValue{}, nil
173+
return []model.LabelEntry{}, nil
174174
}
175175

176-
var result []model.LabelKeyValue
176+
var result []model.LabelEntry
177177
pairs := strings.Split(flagValue, ";")
178178
for _, pair := range pairs {
179179
trimmedPair := strings.TrimSpace(pair)
@@ -184,7 +184,7 @@ func ParseLabelKeyValuePairs(flagValue string) ([]model.LabelKeyValue, error) {
184184
if len(keyValue) != 2 {
185185
return nil, errorutils.CheckErrorf("invalid key-value pair: '%s' (expected format key=value)", pair)
186186
}
187-
result = append(result, model.LabelKeyValue{
187+
result = append(result, model.LabelEntry{
188188
Key: strings.TrimSpace(keyValue[0]),
189189
Value: strings.TrimSpace(keyValue[1]),
190190
})

apptrust/commands/utils/utils_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -169,62 +169,62 @@ func TestParseLabelKeyValuePairs(t *testing.T) {
169169
tests := []struct {
170170
name string
171171
input string
172-
expected []model.LabelKeyValue
172+
expected []model.LabelEntry
173173
expectErr bool
174174
errorMsg string
175175
}{
176176
{
177177
name: "empty string",
178178
input: "",
179-
expected: []model.LabelKeyValue{},
179+
expected: []model.LabelEntry{},
180180
expectErr: false,
181181
},
182182
{
183183
name: "single pair",
184184
input: "key1=value1",
185-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}},
185+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}},
186186
expectErr: false,
187187
},
188188
{
189189
name: "multiple pairs",
190190
input: "key1=value1;key2=value2;key3=value3",
191-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}, {Key: "key3", Value: "value3"}},
191+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}, {Key: "key3", Value: "value3"}},
192192
expectErr: false,
193193
},
194194
{
195195
name: "same key multiple values",
196196
input: "environment=production;environment=staging;region=us-east",
197-
expected: []model.LabelKeyValue{{Key: "environment", Value: "production"}, {Key: "environment", Value: "staging"}, {Key: "region", Value: "us-east"}},
197+
expected: []model.LabelEntry{{Key: "environment", Value: "production"}, {Key: "environment", Value: "staging"}, {Key: "region", Value: "us-east"}},
198198
expectErr: false,
199199
},
200200
{
201201
name: "whitespace handling",
202202
input: " key1 = value1 ; key2 = value2 ",
203-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
203+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
204204
expectErr: false,
205205
},
206206
{
207207
name: "empty pairs skipped",
208208
input: "key1=value1;;key2=value2",
209-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
209+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
210210
expectErr: false,
211211
},
212212
{
213213
name: "leading and trailing separators",
214214
input: ";key1=value1;key2=value2;",
215-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
215+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
216216
expectErr: false,
217217
},
218218
{
219219
name: "empty value",
220220
input: "key1=;key2=value2",
221-
expected: []model.LabelKeyValue{{Key: "key1", Value: ""}, {Key: "key2", Value: "value2"}},
221+
expected: []model.LabelEntry{{Key: "key1", Value: ""}, {Key: "key2", Value: "value2"}},
222222
expectErr: false,
223223
},
224224
{
225225
name: "value with equals sign",
226226
input: "key1=value=with=equals;key2=normal",
227-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value=with=equals"}, {Key: "key2", Value: "normal"}},
227+
expected: []model.LabelEntry{{Key: "key1", Value: "value=with=equals"}, {Key: "key2", Value: "normal"}},
228228
expectErr: false,
229229
},
230230
{
@@ -242,19 +242,19 @@ func TestParseLabelKeyValuePairs(t *testing.T) {
242242
{
243243
name: "empty key",
244244
input: "=value1;key2=value2",
245-
expected: []model.LabelKeyValue{{Key: "", Value: "value1"}, {Key: "key2", Value: "value2"}},
245+
expected: []model.LabelEntry{{Key: "", Value: "value1"}, {Key: "key2", Value: "value2"}},
246246
expectErr: false,
247247
},
248248
{
249249
name: "whitespace only pairs skipped",
250250
input: "key1=value1; ;key2=value2",
251-
expected: []model.LabelKeyValue{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
251+
expected: []model.LabelEntry{{Key: "key1", Value: "value1"}, {Key: "key2", Value: "value2"}},
252252
expectErr: false,
253253
},
254254
{
255255
name: "special characters in key and value",
256256
input: "env-name=prod-env;region=us-east-1",
257-
expected: []model.LabelKeyValue{{Key: "env-name", Value: "prod-env"}, {Key: "region", Value: "us-east-1"}},
257+
expected: []model.LabelEntry{{Key: "env-name", Value: "prod-env"}, {Key: "region", Value: "us-east-1"}},
258258
expectErr: false,
259259
},
260260
}

apptrust/model/app_descriptor.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,25 @@ var (
3030
}
3131
)
3232

33-
type LabelKeyValue struct {
33+
type LabelEntry struct {
3434
Key string `json:"key"`
3535
Value string `json:"value"`
3636
}
3737

3838
type LabelUpdates struct {
39-
Remove []LabelKeyValue `json:"remove,omitempty"`
40-
Add []LabelKeyValue `json:"add,omitempty"`
39+
Remove []LabelEntry `json:"remove,omitempty"`
40+
Add []LabelEntry `json:"add,omitempty"`
4141
}
4242

4343
type AppDescriptor struct {
44-
ApplicationKey string `json:"application_key"`
45-
ApplicationName string `json:"application_name,omitempty"`
46-
ProjectKey string `json:"project_key,omitempty"`
47-
Description *string `json:"description,omitempty"`
48-
MaturityLevel *string `json:"maturity_level,omitempty"`
49-
BusinessCriticality *string `json:"criticality,omitempty"`
50-
Labels *map[string]string `json:"labels,omitempty"`
51-
LabelUpdates *LabelUpdates `json:"label_updates,omitempty"`
52-
UserOwners *[]string `json:"user_owners,omitempty"`
53-
GroupOwners *[]string `json:"group_owners,omitempty"`
44+
ApplicationKey string `json:"application_key"`
45+
ApplicationName string `json:"application_name,omitempty"`
46+
ProjectKey string `json:"project_key,omitempty"`
47+
Description *string `json:"description,omitempty"`
48+
MaturityLevel *string `json:"maturity_level,omitempty"`
49+
BusinessCriticality *string `json:"criticality,omitempty"`
50+
Labels *[]LabelEntry `json:"labels,omitempty"`
51+
LabelUpdates *LabelUpdates `json:"label_updates,omitempty"`
52+
UserOwners *[]string `json:"user_owners,omitempty"`
53+
GroupOwners *[]string `json:"group_owners,omitempty"`
5454
}

0 commit comments

Comments
 (0)