From a381f271aa3a6d7e0d226ab11b142d32ef9be521 Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Thu, 4 Mar 2021 13:29:28 -0500 Subject: [PATCH 1/5] commit Signed-off-by: Maysun J Faisal --- pkg/devfile/parser/data/v2/commands.go | 20 +++ pkg/devfile/parser/data/v2/commands_test.go | 167 +++++++++++------- .../parser/data/v2/common/command_helper.go | 19 ++ pkg/devfile/parser/data/v2/common/options.go | 23 +++ pkg/devfile/parser/data/v2/components.go | 16 +- pkg/devfile/parser/data/v2/components_test.go | 135 ++++++++------ 6 files changed, 256 insertions(+), 124 deletions(-) diff --git a/pkg/devfile/parser/data/v2/commands.go b/pkg/devfile/parser/data/v2/commands.go index bcdb3f49..a242fec4 100644 --- a/pkg/devfile/parser/data/v2/commands.go +++ b/pkg/devfile/parser/data/v2/commands.go @@ -1,6 +1,7 @@ package v2 import ( + "fmt" "strings" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -15,11 +16,30 @@ func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, er var commands []v1.Command for _, command := range d.Commands { + // Filter Attributes filterIn, err := common.FilterDevfileObject(command.Attributes, options) if err != nil { return nil, err } + commandType, err := common.GetCommandType(command) + if err != nil { + return nil, err + } + + if options.CommandOptions.CommandType != "" && commandType != options.CommandOptions.CommandType { + continue + } + + // Filter Command Group Kind + commandGroup := common.GetGroup(command) + fmt.Printf(">>> cmd id is %s, commandGroup is %+v, options.CommandOptions.CommandKind is %+v\n", command.Id, commandGroup, options.CommandOptions.CommandKind) + if commandGroup != nil && options.CommandOptions.CommandKind != "" && options.CommandOptions.CommandKind != commandGroup.Kind { + // fmt.Printf(">>> cmd id is %s, commandGroup.Kind is %+v, options.CommandOptions.CommandKind is %+v\n", command.Id, commandGroup.Kind, options.CommandOptions.CommandKind) + // filterIn = filterIn && true + continue + } + if filterIn { command.Id = strings.ToLower(command.Id) commands = append(commands, command) diff --git a/pkg/devfile/parser/data/v2/commands_test.go b/pkg/devfile/parser/data/v2/commands_test.go index 29d6473d..adb58c47 100644 --- a/pkg/devfile/parser/data/v2/commands_test.go +++ b/pkg/devfile/parser/data/v2/commands_test.go @@ -22,26 +22,26 @@ func TestDevfile200_GetCommands(t *testing.T) { wantCommands []string wantErr bool }{ - { - name: "case 1: get the necessary commands", - currentCommands: []v1.Command{ - { - Id: "command1", - CommandUnion: v1.CommandUnion{ - Exec: &v1.ExecCommand{}, - }, - }, - { - Id: "command2", - CommandUnion: v1.CommandUnion{ - Composite: &v1.CompositeCommand{}, - }, - }, - }, - filterOptions: common.DevfileOptions{}, - wantCommands: []string{"command1", "command2"}, - wantErr: false, - }, + // { + // name: "case 1: get the necessary commands", + // currentCommands: []v1.Command{ + // { + // Id: "command1", + // CommandUnion: v1.CommandUnion{ + // Exec: &v1.ExecCommand{}, + // }, + // }, + // { + // Id: "command2", + // CommandUnion: v1.CommandUnion{ + // Composite: &v1.CompositeCommand{}, + // }, + // }, + // }, + // filterOptions: common.DevfileOptions{}, + // wantCommands: []string{"command1", "command2"}, + // wantErr: false, + // }, { name: "case 2: get the filtered commands", currentCommands: []v1.Command{ @@ -52,7 +52,15 @@ func TestDevfile200_GetCommands(t *testing.T) { "secondString": "secondStringValue", }), CommandUnion: v1.CommandUnion{ - Exec: &v1.ExecCommand{}, + Exec: &v1.ExecCommand{ + LabeledCommand: v1.LabeledCommand{ + BaseCommand: v1.BaseCommand{ + Group: &v1.CommandGroup{ + Kind: v1.BuildCommandGroupKind, + }, + }, + }, + }, }, }, { @@ -65,48 +73,69 @@ func TestDevfile200_GetCommands(t *testing.T) { Composite: &v1.CompositeCommand{}, }, }, - }, - filterOptions: common.DevfileOptions{ - Filter: map[string]interface{}{ - "firstString": "firstStringValue", - "secondString": "secondStringValue", - }, - }, - wantCommands: []string{"command1"}, - wantErr: false, - }, - { - name: "case 3: get the wrong filtered commands", - currentCommands: []v1.Command{ - { - Id: "command1", - Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - "firstString": "firstStringValue", - "secondString": "secondStringValue", - }), - CommandUnion: v1.CommandUnion{ - Exec: &v1.ExecCommand{}, - }, - }, { - Id: "command2", + Id: "command3", Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ "firstString": "firstStringValue", "thirdString": "thirdStringValue", }), CommandUnion: v1.CommandUnion{ - Composite: &v1.CompositeCommand{}, + Composite: &v1.CompositeCommand{ + LabeledCommand: v1.LabeledCommand{ + BaseCommand: v1.BaseCommand{ + Group: &v1.CommandGroup{ + Kind: v1.BuildCommandGroupKind, + }, + }, + }, + }, }, }, }, filterOptions: common.DevfileOptions{ Filter: map[string]interface{}{ - "firstStringIsWrong": "firstStringValue", + "firstString": "firstStringValue", + // "secondString": "secondStringValue", + }, + CommandOptions: common.CommandOptions{ + CommandKind: v1.BuildCommandGroupKind, }, }, - wantCommands: []string{}, + wantCommands: []string{"command1"}, wantErr: false, }, + // { + // name: "case 3: get the wrong filtered commands", + // currentCommands: []v1.Command{ + // { + // Id: "command1", + // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + // "firstString": "firstStringValue", + // "secondString": "secondStringValue", + // }), + // CommandUnion: v1.CommandUnion{ + // Exec: &v1.ExecCommand{}, + // }, + // }, + // { + // Id: "command2", + // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + // "firstString": "firstStringValue", + // "thirdString": "thirdStringValue", + // }), + // CommandUnion: v1.CommandUnion{ + // Composite: &v1.CompositeCommand{}, + // }, + // }, + // }, + // filterOptions: common.DevfileOptions{ + // Filter: map[string]interface{}{ + // "firstStringIsWrong": "firstStringValue", + // }, + // }, + // wantCommands: []string{}, + // wantErr: false, + // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -121,28 +150,32 @@ func TestDevfile200_GetCommands(t *testing.T) { } commands, err := d.GetCommands(tt.filterOptions) - if !tt.wantErr && err != nil { - t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err) - return - } else if tt.wantErr && err == nil { - t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands) - return - } else if tt.wantErr && err != nil { - return + t.Logf(">>> err is %+v", err) + for _, cmd := range commands { + t.Logf("cmd id is %+v", cmd.Id) } + // if !tt.wantErr && err != nil { + // t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err) + // return + // } else if tt.wantErr && err == nil { + // t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands) + // return + // } else if tt.wantErr && err != nil { + // return + // } - for _, wantCommand := range tt.wantCommands { - matched := false - for _, devfileCommand := range commands { - if wantCommand == devfileCommand.Id { - matched = true - } - } + // for _, wantCommand := range tt.wantCommands { + // matched := false + // for _, devfileCommand := range commands { + // if wantCommand == devfileCommand.Id { + // matched = true + // } + // } - if !matched { - t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand) - } - } + // if !matched { + // t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand) + // } + // } }) } } diff --git a/pkg/devfile/parser/data/v2/common/command_helper.go b/pkg/devfile/parser/data/v2/common/command_helper.go index d3e44b49..5cf25934 100644 --- a/pkg/devfile/parser/data/v2/common/command_helper.go +++ b/pkg/devfile/parser/data/v2/common/command_helper.go @@ -1,6 +1,8 @@ package common import ( + "fmt" + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" ) @@ -47,3 +49,20 @@ func GetExecWorkingDir(dc v1.Command) string { return "" } + +// GetCommandType returns the command type of a given command +func GetCommandType(command v1.Command) (v1.CommandType, error) { + switch { + case command.Apply != nil: + return v1.ApplyCommandType, nil + case command.Composite != nil: + return v1.CompositeCommandType, nil + case command.Exec != nil: + return v1.ExecCommandType, nil + case command.Custom != nil: + return v1.CustomCommandType, nil + + default: + return "", fmt.Errorf("unknown component type") + } +} diff --git a/pkg/devfile/parser/data/v2/common/options.go b/pkg/devfile/parser/data/v2/common/options.go index 14d595d0..edbdfb44 100644 --- a/pkg/devfile/parser/data/v2/common/options.go +++ b/pkg/devfile/parser/data/v2/common/options.go @@ -3,6 +3,7 @@ package common import ( "reflect" + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" apiAttributes "github.com/devfile/api/v2/pkg/attributes" ) @@ -10,6 +11,28 @@ import ( type DevfileOptions struct { // Filter is a map that lets you filter devfile object against their attributes. Interface can be string, float, boolean or a map Filter map[string]interface{} + + // CommandOptions specifies the various options available to filter commands + CommandOptions CommandOptions + + // ComponentOptions specifies the various options available to filter components + ComponentOptions ComponentOptions +} + +// CommandOptions specifies the various options available to filter commands +type CommandOptions struct { + // CommandKind is an option that allows you to filter command based on their kind + CommandKind v1.CommandGroupKind + + // CommandType is an option that allows you to filter command based on their type + CommandType v1.CommandType +} + +// ComponentOptions specifies the various options available to filter components +type ComponentOptions struct { + + // ComponentType is an option that allows you to filter component based on their type + ComponentType v1.ComponentType } // FilterDevfileObject filters devfile attributes with the given options diff --git a/pkg/devfile/parser/data/v2/components.go b/pkg/devfile/parser/data/v2/components.go index 0bac4bfd..4d501ff0 100644 --- a/pkg/devfile/parser/data/v2/components.go +++ b/pkg/devfile/parser/data/v2/components.go @@ -12,14 +12,24 @@ func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component } var components []v1.Component - for _, comp := range d.Components { - filterIn, err := common.FilterDevfileObject(comp.Attributes, options) + for _, component := range d.Components { + + componentType, err := common.GetComponentType(component) + if err != nil { + return nil, err + } + + if options.ComponentOptions.ComponentType != "" && componentType != options.ComponentOptions.ComponentType { + continue + } + + filterIn, err := common.FilterDevfileObject(component.Attributes, options) if err != nil { return nil, err } if filterIn { - components = append(components, comp) + components = append(components, component) } } diff --git a/pkg/devfile/parser/data/v2/components_test.go b/pkg/devfile/parser/data/v2/components_test.go index 09db14fe..5d3acc48 100644 --- a/pkg/devfile/parser/data/v2/components_test.go +++ b/pkg/devfile/parser/data/v2/components_test.go @@ -179,31 +179,31 @@ func TestGetDevfileContainerComponents(t *testing.T) { filterOptions common.DevfileOptions wantErr bool }{ - { - name: "Case 1: Invalid devfile", - component: []v1.Component{}, - expectedMatchesCount: 0, - }, - { - name: "Case 2: Valid devfile with wrong component type (Openshift)", - component: []v1.Component{ - { - ComponentUnion: v1.ComponentUnion{ - Openshift: &v1.OpenshiftComponent{}, - }, - }, - }, - expectedMatchesCount: 0, - }, - { - name: "Case 3 : Valid devfile with correct component type (Container)", - component: []v1.Component{ - testingutil.GetFakeContainerComponent("comp1"), - testingutil.GetFakeContainerComponent("comp2"), - }, - expectedMatchesCount: 2, - filterOptions: common.DevfileOptions{}, - }, + // { + // name: "Case 1: Invalid devfile", + // component: []v1.Component{}, + // expectedMatchesCount: 0, + // }, + // { + // name: "Case 2: Valid devfile with wrong component type (Openshift)", + // component: []v1.Component{ + // { + // ComponentUnion: v1.ComponentUnion{ + // Openshift: &v1.OpenshiftComponent{}, + // }, + // }, + // }, + // expectedMatchesCount: 0, + // }, + // { + // name: "Case 3 : Valid devfile with correct component type (Container)", + // component: []v1.Component{ + // testingutil.GetFakeContainerComponent("comp1"), + // testingutil.GetFakeContainerComponent("comp2"), + // }, + // expectedMatchesCount: 2, + // filterOptions: common.DevfileOptions{}, + // }, { name: "Case 4 : Get Container component with the specified filter", component: []v1.Component{ @@ -227,47 +227,69 @@ func TestGetDevfileContainerComponents(t *testing.T) { Container: &v1.ContainerComponent{}, }, }, - }, - filterOptions: common.DevfileOptions{ - Filter: map[string]interface{}{ - "firstString": "firstStringValue", - "secondString": "secondStringValue", - }, - }, - expectedMatchesCount: 1, - }, - { - name: "Case 5 : Get Container component with the wrong specified filter", - component: []v1.Component{ { - Name: "comp1", + Name: "comp3", Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ "firstString": "firstStringValue", - "secondString": "secondStringValue", + "fourthString": "fourthStringValue", }), ComponentUnion: v1.ComponentUnion{ - Container: &v1.ContainerComponent{}, + Volume: &v1.VolumeComponent{}, }, }, { - Name: "comp2", + Name: "comp4", Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - "firstString": "firstStringValue", - "thirdString": "thirdStringValue", + "fourthString": "fourthStringValue", }), ComponentUnion: v1.ComponentUnion{ - Container: &v1.ContainerComponent{}, + Volume: &v1.VolumeComponent{}, }, }, }, filterOptions: common.DevfileOptions{ Filter: map[string]interface{}{ - "firstStringIsWrong": "firstStringValue", + "firstString": "firstStringValue", + // "secondString": "secondStringValue", + }, + ComponentOptions: common.ComponentOptions{ + ComponentType: v1.VolumeComponentType, }, }, - expectedMatchesCount: 0, - wantErr: false, + expectedMatchesCount: 1, }, + // { + // name: "Case 5 : Get Container component with the wrong specified filter", + // component: []v1.Component{ + // { + // Name: "comp1", + // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + // "firstString": "firstStringValue", + // "secondString": "secondStringValue", + // }), + // ComponentUnion: v1.ComponentUnion{ + // Container: &v1.ContainerComponent{}, + // }, + // }, + // { + // Name: "comp2", + // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + // "firstString": "firstStringValue", + // "thirdString": "thirdStringValue", + // }), + // ComponentUnion: v1.ComponentUnion{ + // Container: &v1.ContainerComponent{}, + // }, + // }, + // }, + // filterOptions: common.DevfileOptions{ + // Filter: map[string]interface{}{ + // "firstStringIsWrong": "firstStringValue", + // }, + // }, + // expectedMatchesCount: 0, + // wantErr: false, + // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -281,14 +303,19 @@ func TestGetDevfileContainerComponents(t *testing.T) { }, } - devfileComponents, err := d.GetDevfileContainerComponents(tt.filterOptions) - if !tt.wantErr && err != nil { - t.Errorf("TestGetDevfileContainerComponents unexpected error: %v", err) - } else if tt.wantErr && err == nil { - t.Errorf("TestGetDevfileContainerComponents expected error but got nil") - } else if len(devfileComponents) != tt.expectedMatchesCount { - t.Errorf("TestGetDevfileContainerComponents error: wrong number of components matched: expected %v, actual %v", tt.expectedMatchesCount, len(devfileComponents)) + devfileComponents, err := d.GetComponents(tt.filterOptions) + t.Logf(">>> err is %+v", err) + for _, comp := range devfileComponents { + t.Logf("comp name is %+v", comp.Name) } + + // if !tt.wantErr && err != nil { + // t.Errorf("TestGetDevfileContainerComponents unexpected error: %v", err) + // } else if tt.wantErr && err == nil { + // t.Errorf("TestGetDevfileContainerComponents expected error but got nil") + // } else if len(devfileComponents) != tt.expectedMatchesCount { + // t.Errorf("TestGetDevfileContainerComponents error: wrong number of components matched: expected %v, actual %v", tt.expectedMatchesCount, len(devfileComponents)) + // } }) } From 0ef126f9904b47875fe9599cddc97558fed1978b Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Tue, 16 Mar 2021 12:08:18 -0400 Subject: [PATCH 2/5] Add filter to command and component get Signed-off-by: Maysun J Faisal --- pkg/devfile/parser/data/v2/commands.go | 19 +- pkg/devfile/parser/data/v2/commands_test.go | 169 ++++++++++-------- pkg/devfile/parser/data/v2/common/options.go | 4 +- pkg/devfile/parser/data/v2/components.go | 17 +- pkg/devfile/parser/data/v2/components_test.go | 151 ++++++++-------- 5 files changed, 183 insertions(+), 177 deletions(-) diff --git a/pkg/devfile/parser/data/v2/commands.go b/pkg/devfile/parser/data/v2/commands.go index a242fec4..c2260af6 100644 --- a/pkg/devfile/parser/data/v2/commands.go +++ b/pkg/devfile/parser/data/v2/commands.go @@ -1,7 +1,6 @@ package v2 import ( - "fmt" "strings" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -10,33 +9,31 @@ import ( // GetCommands returns the slice of Command objects parsed from the Devfile func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, error) { - if len(options.Filter) == 0 { - return d.Commands, nil - } var commands []v1.Command for _, command := range d.Commands { - // Filter Attributes + // Filter Command Attributes filterIn, err := common.FilterDevfileObject(command.Attributes, options) if err != nil { return nil, err + } else if !filterIn { + continue } + // Filter Command Type - Exec, Composite, etc. commandType, err := common.GetCommandType(command) if err != nil { return nil, err } - if options.CommandOptions.CommandType != "" && commandType != options.CommandOptions.CommandType { continue } - // Filter Command Group Kind + // Filter Command Group Kind - Run, Build, etc. commandGroup := common.GetGroup(command) - fmt.Printf(">>> cmd id is %s, commandGroup is %+v, options.CommandOptions.CommandKind is %+v\n", command.Id, commandGroup, options.CommandOptions.CommandKind) - if commandGroup != nil && options.CommandOptions.CommandKind != "" && options.CommandOptions.CommandKind != commandGroup.Kind { - // fmt.Printf(">>> cmd id is %s, commandGroup.Kind is %+v, options.CommandOptions.CommandKind is %+v\n", command.Id, commandGroup.Kind, options.CommandOptions.CommandKind) - // filterIn = filterIn && true + if commandGroup != nil && options.CommandOptions.CommandGroupKind != "" && options.CommandOptions.CommandGroupKind != commandGroup.Kind { + continue + } else if commandGroup == nil && options.CommandOptions.CommandGroupKind != "" { continue } diff --git a/pkg/devfile/parser/data/v2/commands_test.go b/pkg/devfile/parser/data/v2/commands_test.go index adb58c47..2e9338e1 100644 --- a/pkg/devfile/parser/data/v2/commands_test.go +++ b/pkg/devfile/parser/data/v2/commands_test.go @@ -22,26 +22,26 @@ func TestDevfile200_GetCommands(t *testing.T) { wantCommands []string wantErr bool }{ - // { - // name: "case 1: get the necessary commands", - // currentCommands: []v1.Command{ - // { - // Id: "command1", - // CommandUnion: v1.CommandUnion{ - // Exec: &v1.ExecCommand{}, - // }, - // }, - // { - // Id: "command2", - // CommandUnion: v1.CommandUnion{ - // Composite: &v1.CompositeCommand{}, - // }, - // }, - // }, - // filterOptions: common.DevfileOptions{}, - // wantCommands: []string{"command1", "command2"}, - // wantErr: false, - // }, + { + name: "case 1: get the necessary commands", + currentCommands: []v1.Command{ + { + Id: "command1", + CommandUnion: v1.CommandUnion{ + Exec: &v1.ExecCommand{}, + }, + }, + { + Id: "command2", + CommandUnion: v1.CommandUnion{ + Composite: &v1.CompositeCommand{}, + }, + }, + }, + filterOptions: common.DevfileOptions{}, + wantCommands: []string{"command1", "command2"}, + wantErr: false, + }, { name: "case 2: get the filtered commands", currentCommands: []v1.Command{ @@ -91,51 +91,68 @@ func TestDevfile200_GetCommands(t *testing.T) { }, }, }, + { + Id: "command4", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "thirdString": "thirdStringValue", + }), + CommandUnion: v1.CommandUnion{ + Apply: &v1.ApplyCommand{ + LabeledCommand: v1.LabeledCommand{ + BaseCommand: v1.BaseCommand{ + Group: &v1.CommandGroup{ + Kind: v1.BuildCommandGroupKind, + }, + }, + }, + }, + }, + }, }, filterOptions: common.DevfileOptions{ Filter: map[string]interface{}{ "firstString": "firstStringValue", - // "secondString": "secondStringValue", }, CommandOptions: common.CommandOptions{ - CommandKind: v1.BuildCommandGroupKind, + CommandGroupKind: v1.BuildCommandGroupKind, + CommandType: v1.CompositeCommandType, + }, + }, + wantCommands: []string{"command3"}, + wantErr: false, + }, + { + name: "case 3: get the wrong filtered commands", + currentCommands: []v1.Command{ + { + Id: "command1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + CommandUnion: v1.CommandUnion{ + Exec: &v1.ExecCommand{}, + }, + }, + { + Id: "command2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + CommandUnion: v1.CommandUnion{ + Composite: &v1.CompositeCommand{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstStringIsWrong": "firstStringValue", }, }, - wantCommands: []string{"command1"}, + wantCommands: []string{}, wantErr: false, }, - // { - // name: "case 3: get the wrong filtered commands", - // currentCommands: []v1.Command{ - // { - // Id: "command1", - // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - // "firstString": "firstStringValue", - // "secondString": "secondStringValue", - // }), - // CommandUnion: v1.CommandUnion{ - // Exec: &v1.ExecCommand{}, - // }, - // }, - // { - // Id: "command2", - // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - // "firstString": "firstStringValue", - // "thirdString": "thirdStringValue", - // }), - // CommandUnion: v1.CommandUnion{ - // Composite: &v1.CompositeCommand{}, - // }, - // }, - // }, - // filterOptions: common.DevfileOptions{ - // Filter: map[string]interface{}{ - // "firstStringIsWrong": "firstStringValue", - // }, - // }, - // wantCommands: []string{}, - // wantErr: false, - // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -150,32 +167,28 @@ func TestDevfile200_GetCommands(t *testing.T) { } commands, err := d.GetCommands(tt.filterOptions) - t.Logf(">>> err is %+v", err) - for _, cmd := range commands { - t.Logf("cmd id is %+v", cmd.Id) + if !tt.wantErr && err != nil { + t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err) + return + } else if tt.wantErr && err == nil { + t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands) + return + } else if tt.wantErr && err != nil { + return } - // if !tt.wantErr && err != nil { - // t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err) - // return - // } else if tt.wantErr && err == nil { - // t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands) - // return - // } else if tt.wantErr && err != nil { - // return - // } - // for _, wantCommand := range tt.wantCommands { - // matched := false - // for _, devfileCommand := range commands { - // if wantCommand == devfileCommand.Id { - // matched = true - // } - // } + for _, devfileCommand := range commands { + matched := false + for _, wantCommand := range tt.wantCommands { + if wantCommand == devfileCommand.Id { + matched = true + } + } - // if !matched { - // t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand) - // } - // } + if !matched { + t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the expected list", devfileCommand.Id) + } + } }) } } diff --git a/pkg/devfile/parser/data/v2/common/options.go b/pkg/devfile/parser/data/v2/common/options.go index edbdfb44..139d5627 100644 --- a/pkg/devfile/parser/data/v2/common/options.go +++ b/pkg/devfile/parser/data/v2/common/options.go @@ -21,8 +21,8 @@ type DevfileOptions struct { // CommandOptions specifies the various options available to filter commands type CommandOptions struct { - // CommandKind is an option that allows you to filter command based on their kind - CommandKind v1.CommandGroupKind + // CommandGroupKind is an option that allows you to filter command based on their kind + CommandGroupKind v1.CommandGroupKind // CommandType is an option that allows you to filter command based on their type CommandType v1.CommandType diff --git a/pkg/devfile/parser/data/v2/components.go b/pkg/devfile/parser/data/v2/components.go index 4d501ff0..a79370a2 100644 --- a/pkg/devfile/parser/data/v2/components.go +++ b/pkg/devfile/parser/data/v2/components.go @@ -7,26 +7,25 @@ import ( // GetComponents returns the slice of Component objects parsed from the Devfile func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component, error) { - if len(options.Filter) == 0 { - return d.Components, nil - } var components []v1.Component for _, component := range d.Components { - - componentType, err := common.GetComponentType(component) + // Filter Component Attributes + filterIn, err := common.FilterDevfileObject(component.Attributes, options) if err != nil { return nil, err - } - - if options.ComponentOptions.ComponentType != "" && componentType != options.ComponentOptions.ComponentType { + } else if !filterIn { continue } - filterIn, err := common.FilterDevfileObject(component.Attributes, options) + // Filter Component Type - Container, Volume, etc. + componentType, err := common.GetComponentType(component) if err != nil { return nil, err } + if options.ComponentOptions.ComponentType != "" && componentType != options.ComponentOptions.ComponentType { + continue + } if filterIn { components = append(components, component) diff --git a/pkg/devfile/parser/data/v2/components_test.go b/pkg/devfile/parser/data/v2/components_test.go index 5d3acc48..78cefa10 100644 --- a/pkg/devfile/parser/data/v2/components_test.go +++ b/pkg/devfile/parser/data/v2/components_test.go @@ -170,42 +170,22 @@ func TestDevfile200_UpdateComponent(t *testing.T) { } } -func TestGetDevfileContainerComponents(t *testing.T) { +func TestGetDevfileComponents(t *testing.T) { tests := []struct { - name string - component []v1.Component - expectedMatchesCount int - filterOptions common.DevfileOptions - wantErr bool + name string + component []v1.Component + wantComponents []string + filterOptions common.DevfileOptions + wantErr bool }{ - // { - // name: "Case 1: Invalid devfile", - // component: []v1.Component{}, - // expectedMatchesCount: 0, - // }, - // { - // name: "Case 2: Valid devfile with wrong component type (Openshift)", - // component: []v1.Component{ - // { - // ComponentUnion: v1.ComponentUnion{ - // Openshift: &v1.OpenshiftComponent{}, - // }, - // }, - // }, - // expectedMatchesCount: 0, - // }, - // { - // name: "Case 3 : Valid devfile with correct component type (Container)", - // component: []v1.Component{ - // testingutil.GetFakeContainerComponent("comp1"), - // testingutil.GetFakeContainerComponent("comp2"), - // }, - // expectedMatchesCount: 2, - // filterOptions: common.DevfileOptions{}, - // }, { - name: "Case 4 : Get Container component with the specified filter", + name: "Invalid devfile", + component: []v1.Component{}, + wantComponents: nil, + }, + { + name: "Get component with the specified filter", component: []v1.Component{ { Name: "comp1", @@ -250,46 +230,52 @@ func TestGetDevfileContainerComponents(t *testing.T) { filterOptions: common.DevfileOptions{ Filter: map[string]interface{}{ "firstString": "firstStringValue", - // "secondString": "secondStringValue", + }, + CommandOptions: common.CommandOptions{ + CommandGroupKind: v1.BuildCommandGroupKind, + CommandType: v1.CompositeCommandType, }, ComponentOptions: common.ComponentOptions{ ComponentType: v1.VolumeComponentType, }, }, - expectedMatchesCount: 1, + wantComponents: []string{"comp3"}, + }, + { + name: "Get component with the wrong specified filter", + component: []v1.Component{ + { + Name: "comp1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + { + Name: "comp2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstStringIsWrong": "firstStringValue", + }, + ComponentOptions: common.ComponentOptions{ + ComponentType: v1.ContainerComponentType, + }, + }, + wantComponents: []string{""}, + wantErr: false, }, - // { - // name: "Case 5 : Get Container component with the wrong specified filter", - // component: []v1.Component{ - // { - // Name: "comp1", - // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - // "firstString": "firstStringValue", - // "secondString": "secondStringValue", - // }), - // ComponentUnion: v1.ComponentUnion{ - // Container: &v1.ContainerComponent{}, - // }, - // }, - // { - // Name: "comp2", - // Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ - // "firstString": "firstStringValue", - // "thirdString": "thirdStringValue", - // }), - // ComponentUnion: v1.ComponentUnion{ - // Container: &v1.ContainerComponent{}, - // }, - // }, - // }, - // filterOptions: common.DevfileOptions{ - // Filter: map[string]interface{}{ - // "firstStringIsWrong": "firstStringValue", - // }, - // }, - // expectedMatchesCount: 0, - // wantErr: false, - // }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -303,19 +289,30 @@ func TestGetDevfileContainerComponents(t *testing.T) { }, } - devfileComponents, err := d.GetComponents(tt.filterOptions) - t.Logf(">>> err is %+v", err) - for _, comp := range devfileComponents { - t.Logf("comp name is %+v", comp.Name) + components, err := d.GetComponents(tt.filterOptions) + + if !tt.wantErr && err != nil { + t.Errorf("TestGetDevfileComponents() unexpected error - %v", err) + return + } else if tt.wantErr && err == nil { + t.Errorf("TestGetDevfileComponents() expected an error but got nil %v", components) + return + } else if tt.wantErr && err != nil { + return } - // if !tt.wantErr && err != nil { - // t.Errorf("TestGetDevfileContainerComponents unexpected error: %v", err) - // } else if tt.wantErr && err == nil { - // t.Errorf("TestGetDevfileContainerComponents expected error but got nil") - // } else if len(devfileComponents) != tt.expectedMatchesCount { - // t.Errorf("TestGetDevfileContainerComponents error: wrong number of components matched: expected %v, actual %v", tt.expectedMatchesCount, len(devfileComponents)) - // } + for _, devfileComponent := range components { + matched := false + for _, wantComponent := range tt.wantComponents { + if wantComponent == devfileComponent.Name { + matched = true + } + } + + if !matched { + t.Errorf("TestGetDevfileComponents() error - component %s not found in the expected list", devfileComponent.Name) + } + } }) } From 9919624930a10d88a68ec730503899ed43f7e698 Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Tue, 16 Mar 2021 16:49:53 -0400 Subject: [PATCH 3/5] Update devfile options Signed-off-by: Maysun J Faisal --- README.md | 69 +++-- pkg/devfile/parser/data/interface.go | 2 +- pkg/devfile/parser/data/v2/commands.go | 6 +- pkg/devfile/parser/data/v2/commands_test.go | 51 +-- .../parser/data/v2/common/command_helper.go | 2 +- .../data/v2/common/command_helper_test.go | 77 +++++ .../data/v2/common/component_helper_test.go | 50 +-- pkg/devfile/parser/data/v2/common/options.go | 18 +- .../parser/data/v2/common/project_helper.go | 17 + .../data/v2/common/project_helper_test.go | 62 ++++ pkg/devfile/parser/data/v2/components.go | 8 +- pkg/devfile/parser/data/v2/components_test.go | 172 ++++++++-- pkg/devfile/parser/data/v2/projects.go | 46 ++- pkg/devfile/parser/data/v2/projects_test.go | 293 ++++++++++++++++++ 14 files changed, 748 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index 8f717367..edae7673 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Devfile Parser Library +# Devfile Library ## About @@ -8,25 +8,43 @@ The Devfile Parser library is a Golang module that: 3. generates Kubernetes objects for the various devfile resources. 4. defines util functions for the devfile. +## Usage + The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/github.com/devfile/library). 1. To parse a devfile, visit pkg/devfile/parse.go - ``` - // Parses the devfile and validates the devfile data - devfile, err := devfilePkg.ParseAndValidate(devfileLocation) - - // To get all the components from the devfile - components, err := devfile.Data.GetComponents(DevfileOptions{}) - - // To get all the components from the devfile with attributes tagged - tool: console-import & import: {strategy: Dockerfile} - components, err := devfile.Data.GetComponents(DevfileOptions{ - Filter: map[string]interface{}{ - "tool": "console-import", - "import": map[string]interface{}{ - "strategy": "Dockerfile", - }, + ``` + // Parses the devfile and validates the devfile data + devfile, err := devfilePkg.ParseAndValidate(devfileLocation) + + // To get all the components from the devfile + components, err := devfile.Data.GetComponents(DevfileOptions{}) + + // To get all the components from the devfile with attributes tagged - tool: console-import + // & import: {strategy: Dockerfile} + components, err := devfile.Data.GetComponents(DevfileOptions{ + Filter: map[string]interface{}{ + "tool": "console-import", + "import": map[string]interface{}{ + "strategy": "Dockerfile", }, - }) - ``` + }, + }) + + // To get all the volume components + components, err := devfile.Data.GetComponents(DevfileOptions{ + ComponentOptions: ComponentOptions{ + ComponentType: v1.VolumeComponentType, + }, + }) + + // To get all the exec commands that belong to the build group + commands, err := devfile.Data.GetCommands(DevfileOptions{ + CommandOptions: CommandOptions{ + CommandType: v1.ExecCommandType, + CommandGroupKind: v1.BuildCommandGroupKind, + }, + }) + ``` 2. To get the Kubernetes objects from the devfile, visit pkg/devfile/generator/generators.go ``` // To get a slice of Kubernetes containers of type corev1.Container from the devfile component containers @@ -43,8 +61,9 @@ The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/g } deployment := generator.GetDeployment(deployParams) ``` - -

+ +## Updating Library Schema + Run `updateApi.sh` can update to use latest `github.com/devfile/api` and update the schema saved under `pkg/devfile/parser/data` The script also accepts version number as an argument to update devfile schema for a specific devfile version. @@ -54,15 +73,19 @@ For example, run the following command will update devfile schema for 2.0.0 ``` Running the script with no arguments will default to update the latest devfile version +## Projects using devfile/library -## Usage - -In the future, the following projects will be consuming this library as a Golang dependency +The following projects are consuming this library as a Golang dependency -* [Workspace Operator](https://github.com/devfile/devworkspace-operator) * [odo](https://github.com/openshift/odo) * [OpenShift Console](https://github.com/openshift/console) +In the future, [Workspace Operator](https://github.com/devfile/devworkspace-operator) will be the next consumer of devfile/library. + ## Issues Issues are tracked in the [devfile/api](https://github.com/devfile/api) repo with the label [area/library](https://github.com/devfile/api/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Flibrary) + +## Releases + +For devfile/library releases, please check the release [page](https://github.com/devfile/library/releases). diff --git a/pkg/devfile/parser/data/interface.go b/pkg/devfile/parser/data/interface.go index 3086b78d..dce8e56c 100644 --- a/pkg/devfile/parser/data/interface.go +++ b/pkg/devfile/parser/data/interface.go @@ -55,7 +55,7 @@ type DevfileData interface { GetDevfileWorkspace() *v1.DevWorkspaceTemplateSpecContent SetDevfileWorkspace(content v1.DevWorkspaceTemplateSpecContent) - //utils + // utils GetDevfileContainerComponents(common.DevfileOptions) ([]v1.Component, error) GetDevfileVolumeComponents(common.DevfileOptions) ([]v1.Component, error) } diff --git a/pkg/devfile/parser/data/v2/commands.go b/pkg/devfile/parser/data/v2/commands.go index c2260af6..48ded785 100644 --- a/pkg/devfile/parser/data/v2/commands.go +++ b/pkg/devfile/parser/data/v2/commands.go @@ -37,10 +37,8 @@ func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, er continue } - if filterIn { - command.Id = strings.ToLower(command.Id) - commands = append(commands, command) - } + command.Id = strings.ToLower(command.Id) + commands = append(commands, command) } return commands, nil diff --git a/pkg/devfile/parser/data/v2/commands_test.go b/pkg/devfile/parser/data/v2/commands_test.go index 2e9338e1..9cbc0951 100644 --- a/pkg/devfile/parser/data/v2/commands_test.go +++ b/pkg/devfile/parser/data/v2/commands_test.go @@ -12,9 +12,6 @@ import ( func TestDevfile200_GetCommands(t *testing.T) { - type args struct { - name string - } tests := []struct { name string currentCommands []v1.Command @@ -23,7 +20,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr bool }{ { - name: "case 1: get the necessary commands", + name: "Get the necessary commands", currentCommands: []v1.Command{ { Id: "command1", @@ -43,7 +40,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr: false, }, { - name: "case 2: get the filtered commands", + name: "Get the filtered commands", currentCommands: []v1.Command{ { Id: "command1", @@ -122,7 +119,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr: false, }, { - name: "case 3: get the wrong filtered commands", + name: "Get the wrong filtered commands", currentCommands: []v1.Command{ { Id: "command1", @@ -150,8 +147,17 @@ func TestDevfile200_GetCommands(t *testing.T) { "firstStringIsWrong": "firstStringValue", }, }, - wantCommands: []string{}, - wantErr: false, + wantErr: false, + }, + { + name: "Wrong command type", + currentCommands: []v1.Command{ + { + Id: "command1", + CommandUnion: v1.CommandUnion{}, + }, + }, + wantErr: true, }, } for _, tt := range tests { @@ -167,26 +173,23 @@ func TestDevfile200_GetCommands(t *testing.T) { } commands, err := d.GetCommands(tt.filterOptions) - if !tt.wantErr && err != nil { - t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err) - return - } else if tt.wantErr && err == nil { - t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands) - return - } else if tt.wantErr && err != nil { + if (err != nil) != tt.wantErr { + t.Errorf("TestDevfile200_GetCommands() error = %v, wantErr %v", err, tt.wantErr) return - } + } else if err == nil { + assert.Equal(t, len(tt.wantCommands), len(commands), "expected length not the same as returned length") - for _, devfileCommand := range commands { - matched := false - for _, wantCommand := range tt.wantCommands { - if wantCommand == devfileCommand.Id { - matched = true + for _, devfileCommand := range commands { + matched := false + for _, wantCommand := range tt.wantCommands { + if wantCommand == devfileCommand.Id { + matched = true + } } - } - if !matched { - t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the expected list", devfileCommand.Id) + if !matched { + t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the expected list", devfileCommand.Id) + } } } }) diff --git a/pkg/devfile/parser/data/v2/common/command_helper.go b/pkg/devfile/parser/data/v2/common/command_helper.go index 5cf25934..6a93ac93 100644 --- a/pkg/devfile/parser/data/v2/common/command_helper.go +++ b/pkg/devfile/parser/data/v2/common/command_helper.go @@ -63,6 +63,6 @@ func GetCommandType(command v1.Command) (v1.CommandType, error) { return v1.CustomCommandType, nil default: - return "", fmt.Errorf("unknown component type") + return "", fmt.Errorf("unknown command type") } } diff --git a/pkg/devfile/parser/data/v2/common/command_helper_test.go b/pkg/devfile/parser/data/v2/common/command_helper_test.go index e3391db3..16fa14bd 100644 --- a/pkg/devfile/parser/data/v2/common/command_helper_test.go +++ b/pkg/devfile/parser/data/v2/common/command_helper_test.go @@ -256,3 +256,80 @@ func TestGetExecWorkingDir(t *testing.T) { } } + +func TestGetCommandType(t *testing.T) { + + tests := []struct { + name string + command v1.Command + wantErr bool + commandType v1.CommandType + }{ + { + name: "Exec command", + command: v1.Command{ + Id: "exec1", + CommandUnion: v1.CommandUnion{ + Exec: &v1.ExecCommand{}, + }, + }, + commandType: v1.ExecCommandType, + wantErr: false, + }, + { + name: "Composite command", + command: v1.Command{ + Id: "comp1", + CommandUnion: v1.CommandUnion{ + Composite: &v1.CompositeCommand{}, + }, + }, + commandType: v1.CompositeCommandType, + wantErr: false, + }, + { + name: "Apply command", + command: v1.Command{ + Id: "apply1", + CommandUnion: v1.CommandUnion{ + Apply: &v1.ApplyCommand{}, + }, + }, + commandType: v1.ApplyCommandType, + wantErr: false, + }, + { + name: "Custom command", + command: v1.Command{ + Id: "custom", + CommandUnion: v1.CommandUnion{ + Custom: &v1.CustomCommand{}, + }, + }, + commandType: v1.CustomCommandType, + wantErr: false, + }, + { + name: "Unknown command", + command: v1.Command{ + Id: "unknown", + CommandUnion: v1.CommandUnion{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetCommandType(tt.command) + // Unexpected error + if (err != nil) != tt.wantErr { + t.Errorf("TestGetCommandType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.commandType { + t.Errorf("TestGetCommandType error: command type mismatch, expected: %v got: %v", tt.commandType, got) + } + }) + } + +} diff --git a/pkg/devfile/parser/data/v2/common/component_helper_test.go b/pkg/devfile/parser/data/v2/common/component_helper_test.go index 6fb5896e..76004ced 100644 --- a/pkg/devfile/parser/data/v2/common/component_helper_test.go +++ b/pkg/devfile/parser/data/v2/common/component_helper_test.go @@ -91,13 +91,13 @@ func TestIsVolume(t *testing.T) { func TestGetComponentType(t *testing.T) { tests := []struct { - name string - component v1.Component - wantErr bool - componentype v1.ComponentType + name string + component v1.Component + wantErr bool + componentType v1.ComponentType }{ { - name: "Case 1: Volume component", + name: "Volume component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ @@ -106,66 +106,66 @@ func TestGetComponentType(t *testing.T) { }, }, }, - componentype: v1.VolumeComponentType, - wantErr: false, + componentType: v1.VolumeComponentType, + wantErr: false, }, { - name: "Case 2: Openshift component", + name: "Openshift component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ Openshift: &v1.OpenshiftComponent{}, }, }, - componentype: v1.OpenshiftComponentType, - wantErr: false, + componentType: v1.OpenshiftComponentType, + wantErr: false, }, { - name: "Case 3: Kubernetes component", + name: "Kubernetes component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ Kubernetes: &v1.KubernetesComponent{}, }, }, - componentype: v1.KubernetesComponentType, - wantErr: false, + componentType: v1.KubernetesComponentType, + wantErr: false, }, { - name: "Case 4: Container component", + name: "Container component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ Container: &v1.ContainerComponent{}, }, }, - componentype: v1.ContainerComponentType, - wantErr: false, + componentType: v1.ContainerComponentType, + wantErr: false, }, { - name: "Case 5: Plugin component", + name: "Plugin component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ Plugin: &v1.PluginComponent{}, }, }, - componentype: v1.PluginComponentType, - wantErr: false, + componentType: v1.PluginComponentType, + wantErr: false, }, { - name: "Case 6: Custom component", + name: "Custom component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{ Custom: &v1.CustomComponent{}, }, }, - componentype: v1.CustomComponentType, - wantErr: false, + componentType: v1.CustomComponentType, + wantErr: false, }, { - name: "Case 6: unknown component", + name: "Unknown component", component: v1.Component{ Name: "name", ComponentUnion: v1.ComponentUnion{}, @@ -181,8 +181,8 @@ func TestGetComponentType(t *testing.T) { t.Errorf("TestGetComponentType() error = %v, wantErr %v", err, tt.wantErr) return } - if got != tt.componentype { - t.Errorf("TestGetComponentType error: component type mismatch, expected: %v got: %v", tt.componentype, got) + if got != tt.componentType { + t.Errorf("TestGetComponentType error: component type mismatch, expected: %v got: %v", tt.componentType, got) } }) } diff --git a/pkg/devfile/parser/data/v2/common/options.go b/pkg/devfile/parser/data/v2/common/options.go index 139d5627..b1443151 100644 --- a/pkg/devfile/parser/data/v2/common/options.go +++ b/pkg/devfile/parser/data/v2/common/options.go @@ -9,7 +9,7 @@ import ( // DevfileOptions provides options for Devfile operations type DevfileOptions struct { - // Filter is a map that lets you filter devfile object against their attributes. Interface can be string, float, boolean or a map + // Filter is a map that lets filter devfile object against their attributes. Interface can be string, float, boolean or a map Filter map[string]interface{} // CommandOptions specifies the various options available to filter commands @@ -17,24 +17,34 @@ type DevfileOptions struct { // ComponentOptions specifies the various options available to filter components ComponentOptions ComponentOptions + + // ProjectOptions specifies the various options available to filter projects/starterProjects + ProjectOptions ProjectOptions } // CommandOptions specifies the various options available to filter commands type CommandOptions struct { - // CommandGroupKind is an option that allows you to filter command based on their kind + // CommandGroupKind is an option that allows to filter command based on their kind CommandGroupKind v1.CommandGroupKind - // CommandType is an option that allows you to filter command based on their type + // CommandType is an option that allows to filter command based on their type CommandType v1.CommandType } // ComponentOptions specifies the various options available to filter components type ComponentOptions struct { - // ComponentType is an option that allows you to filter component based on their type + // ComponentType is an option that allows to filter component based on their type ComponentType v1.ComponentType } +// ProjectOptions specifies the various options available to filter projects/starterProjects +type ProjectOptions struct { + + // ProjectSourceType is an option that allows to filter project based on their source type + ProjectSourceType v1.ProjectSourceType +} + // FilterDevfileObject filters devfile attributes with the given options func FilterDevfileObject(attributes apiAttributes.Attributes, options DevfileOptions) (bool, error) { filterIn := true diff --git a/pkg/devfile/parser/data/v2/common/project_helper.go b/pkg/devfile/parser/data/v2/common/project_helper.go index 4028779b..7d755af1 100644 --- a/pkg/devfile/parser/data/v2/common/project_helper.go +++ b/pkg/devfile/parser/data/v2/common/project_helper.go @@ -41,3 +41,20 @@ func GetDefaultSource(ps v1.GitLikeProjectSource) (remoteName string, remoteURL return remoteName, remoteURL, revision, err } + +// GetProjectSourceType returns the source type of a given project +func GetProjectSourceType(projectSrc v1.ProjectSource) (v1.ProjectSourceType, error) { + switch { + case projectSrc.Git != nil: + return v1.GitProjectSourceType, nil + case projectSrc.Github != nil: + return v1.GitHubProjectSourceType, nil + case projectSrc.Zip != nil: + return v1.ZipProjectSourceType, nil + case projectSrc.Custom != nil: + return v1.CustomProjectSourceType, nil + + default: + return "", fmt.Errorf("unknown project source type") + } +} diff --git a/pkg/devfile/parser/data/v2/common/project_helper_test.go b/pkg/devfile/parser/data/v2/common/project_helper_test.go index 109b560e..397a3adb 100644 --- a/pkg/devfile/parser/data/v2/common/project_helper_test.go +++ b/pkg/devfile/parser/data/v2/common/project_helper_test.go @@ -117,3 +117,65 @@ func TestGitLikeProjectSource_GetDefaultSource(t *testing.T) { }) } } + +func TestGetProjectSrcType(t *testing.T) { + + tests := []struct { + name string + projectSrc v1.ProjectSource + wantErr bool + projectSrcType v1.ProjectSourceType + }{ + { + name: "Git project", + projectSrc: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + projectSrcType: v1.GitProjectSourceType, + wantErr: false, + }, + { + name: "Github project", + projectSrc: v1.ProjectSource{ + Github: &v1.GithubProjectSource{}, + }, + projectSrcType: v1.GitHubProjectSourceType, + wantErr: false, + }, + { + name: "Zip project", + projectSrc: v1.ProjectSource{ + Zip: &v1.ZipProjectSource{}, + }, + projectSrcType: v1.ZipProjectSourceType, + wantErr: false, + }, + { + name: "Custom project", + projectSrc: v1.ProjectSource{ + Custom: &v1.CustomProjectSource{}, + }, + projectSrcType: v1.CustomProjectSourceType, + wantErr: false, + }, + { + name: "Unknown project", + projectSrc: v1.ProjectSource{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetProjectSourceType(tt.projectSrc) + // Unexpected error + if (err != nil) != tt.wantErr { + t.Errorf("TestGetProjectSrcType() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.projectSrcType { + t.Errorf("TestGetProjectSrcType error: project src type mismatch, expected: %v got: %v", tt.projectSrcType, got) + } + }) + } + +} diff --git a/pkg/devfile/parser/data/v2/components.go b/pkg/devfile/parser/data/v2/components.go index a79370a2..fdd924d4 100644 --- a/pkg/devfile/parser/data/v2/components.go +++ b/pkg/devfile/parser/data/v2/components.go @@ -27,15 +27,13 @@ func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component continue } - if filterIn { - components = append(components, component) - } + components = append(components, component) } return components, nil } -// GetDevfileContainerComponents iterates through the components in the devfile and returns a list of devfile container components +// GetDevfileContainerComponents iterates through the components in the devfile and returns a list of devfile container components. Deprecated, use GetComponents() with the DevfileOptions. func (d *DevfileV2) GetDevfileContainerComponents(options common.DevfileOptions) ([]v1.Component, error) { var components []v1.Component devfileComponents, err := d.GetComponents(options) @@ -50,7 +48,7 @@ func (d *DevfileV2) GetDevfileContainerComponents(options common.DevfileOptions) return components, nil } -// GetDevfileVolumeComponents iterates through the components in the devfile and returns a list of devfile volume components +// GetDevfileVolumeComponents iterates through the components in the devfile and returns a list of devfile volume components. Deprecated, use GetComponents() with the DevfileOptions. func (d *DevfileV2) GetDevfileVolumeComponents(options common.DevfileOptions) ([]v1.Component, error) { var components []v1.Component devfileComponents, err := d.GetComponents(options) diff --git a/pkg/devfile/parser/data/v2/components_test.go b/pkg/devfile/parser/data/v2/components_test.go index 78cefa10..33ad90d1 100644 --- a/pkg/devfile/parser/data/v2/components_test.go +++ b/pkg/devfile/parser/data/v2/components_test.go @@ -180,9 +180,8 @@ func TestGetDevfileComponents(t *testing.T) { wantErr bool }{ { - name: "Invalid devfile", - component: []v1.Component{}, - wantComponents: nil, + name: "Invalid devfile", + component: []v1.Component{}, }, { name: "Get component with the specified filter", @@ -273,8 +272,17 @@ func TestGetDevfileComponents(t *testing.T) { ComponentType: v1.ContainerComponentType, }, }, - wantComponents: []string{""}, - wantErr: false, + wantErr: false, + }, + { + name: "Wrong component type", + component: []v1.Component{ + { + Name: "comp1", + ComponentUnion: v1.ComponentUnion{}, + }, + }, + wantErr: true, }, } for _, tt := range tests { @@ -290,27 +298,23 @@ func TestGetDevfileComponents(t *testing.T) { } components, err := d.GetComponents(tt.filterOptions) - - if !tt.wantErr && err != nil { - t.Errorf("TestGetDevfileComponents() unexpected error - %v", err) - return - } else if tt.wantErr && err == nil { - t.Errorf("TestGetDevfileComponents() expected an error but got nil %v", components) - return - } else if tt.wantErr && err != nil { + if (err != nil) != tt.wantErr { + t.Errorf("TestGetDevfileComponents() error = %v, wantErr %v", err, tt.wantErr) return - } + } else if err == nil { + assert.Equal(t, len(tt.wantComponents), len(components), "expected length not the same as returned length") - for _, devfileComponent := range components { - matched := false - for _, wantComponent := range tt.wantComponents { - if wantComponent == devfileComponent.Name { - matched = true + for _, devfileComponent := range components { + matched := false + for _, wantComponent := range tt.wantComponents { + if wantComponent == devfileComponent.Name { + matched = true + } } - } - if !matched { - t.Errorf("TestGetDevfileComponents() error - component %s not found in the expected list", devfileComponent.Name) + if !matched { + t.Errorf("TestGetDevfileComponents() error - component %s not found in the expected list", devfileComponent.Name) + } } } }) @@ -318,6 +322,130 @@ func TestGetDevfileComponents(t *testing.T) { } +func TestGetDevfileContainerComponents(t *testing.T) { + + tests := []struct { + name string + component []v1.Component + expectedMatchesCount int + filterOptions common.DevfileOptions + wantErr bool + }{ + { + name: "Case 1: Invalid devfile", + component: []v1.Component{}, + expectedMatchesCount: 0, + }, + { + name: "Case 2: Valid devfile with wrong component type (Openshift)", + component: []v1.Component{ + { + ComponentUnion: v1.ComponentUnion{ + Openshift: &v1.OpenshiftComponent{}, + }, + }, + }, + expectedMatchesCount: 0, + }, + { + name: "Case 3 : Valid devfile with correct component type (Container)", + component: []v1.Component{ + testingutil.GetFakeContainerComponent("comp1"), + testingutil.GetFakeContainerComponent("comp2"), + }, + expectedMatchesCount: 2, + filterOptions: common.DevfileOptions{}, + }, + { + name: "Case 4 : Get Container component with the specified filter", + component: []v1.Component{ + { + Name: "comp1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + { + Name: "comp2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }, + }, + expectedMatchesCount: 1, + }, + { + name: "Case 5 : Get Container component with the wrong specified filter", + component: []v1.Component{ + { + Name: "comp1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + { + Name: "comp2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstStringIsWrong": "firstStringValue", + }, + }, + expectedMatchesCount: 0, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &DevfileV2{ + v1.Devfile{ + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: tt.component, + }, + }, + }, + } + + devfileComponents, err := d.GetDevfileContainerComponents(tt.filterOptions) + if !tt.wantErr && err != nil { + t.Errorf("TestGetDevfileContainerComponents unexpected error: %v", err) + } else if tt.wantErr && err == nil { + t.Errorf("TestGetDevfileContainerComponents expected error but got nil") + } else if len(devfileComponents) != tt.expectedMatchesCount { + t.Errorf("TestGetDevfileContainerComponents error: wrong number of components matched: expected %v, actual %v", tt.expectedMatchesCount, len(devfileComponents)) + } + }) + } + +} + func TestGetDevfileVolumeComponents(t *testing.T) { tests := []struct { diff --git a/pkg/devfile/parser/data/v2/projects.go b/pkg/devfile/parser/data/v2/projects.go index 62185bee..bdfffe9b 100644 --- a/pkg/devfile/parser/data/v2/projects.go +++ b/pkg/devfile/parser/data/v2/projects.go @@ -9,20 +9,27 @@ import ( // GetProjects returns the Project Object parsed from devfile func (d *DevfileV2) GetProjects(options common.DevfileOptions) ([]v1.Project, error) { - if len(options.Filter) == 0 { - return d.Projects, nil - } - var projects []v1.Project - for _, proj := range d.Projects { - filterIn, err := common.FilterDevfileObject(proj.Attributes, options) + + for _, project := range d.Projects { + // Filter Project Attributes + filterIn, err := common.FilterDevfileObject(project.Attributes, options) if err != nil { return nil, err + } else if !filterIn { + continue } - if filterIn { - projects = append(projects, proj) + // Filter Project Source Type - Git, Zip, etc. + projectSourceType, err := common.GetProjectSourceType(project.ProjectSource) + if err != nil { + return nil, err } + if options.ProjectOptions.ProjectSourceType != "" && projectSourceType != options.ProjectOptions.ProjectSourceType { + continue + } + + projects = append(projects, project) } return projects, nil @@ -73,20 +80,27 @@ func (d *DevfileV2) DeleteProject(name string) error { //GetStarterProjects returns the DevfileStarterProject parsed from devfile func (d *DevfileV2) GetStarterProjects(options common.DevfileOptions) ([]v1.StarterProject, error) { - if len(options.Filter) == 0 { - return d.StarterProjects, nil - } - var starterProjects []v1.StarterProject - for _, starterProj := range d.StarterProjects { - filterIn, err := common.FilterDevfileObject(starterProj.Attributes, options) + + for _, starterProject := range d.StarterProjects { + // Filter Starter Project Attributes + filterIn, err := common.FilterDevfileObject(starterProject.Attributes, options) if err != nil { return nil, err + } else if !filterIn { + continue } - if filterIn { - starterProjects = append(starterProjects, starterProj) + // Filter Starter Project Source Type - Git, Zip, etc. + starterProjectSourceType, err := common.GetProjectSourceType(starterProject.ProjectSource) + if err != nil { + return nil, err + } + if options.ProjectOptions.ProjectSourceType != "" && starterProjectSourceType != options.ProjectOptions.ProjectSourceType { + continue } + + starterProjects = append(starterProjects, starterProject) } return starterProjects, nil diff --git a/pkg/devfile/parser/data/v2/projects_test.go b/pkg/devfile/parser/data/v2/projects_test.go index d3455f7e..1c5315d9 100644 --- a/pkg/devfile/parser/data/v2/projects_test.go +++ b/pkg/devfile/parser/data/v2/projects_test.go @@ -5,10 +5,158 @@ import ( "testing" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" + "github.com/devfile/api/v2/pkg/attributes" + "github.com/devfile/library/pkg/devfile/parser/data/v2/common" "github.com/kylelemons/godebug/pretty" "github.com/stretchr/testify/assert" ) +func TestDevfile200_GetProjects(t *testing.T) { + + tests := []struct { + name string + currentProjects []v1.Project + filterOptions common.DevfileOptions + wantProjects []string + wantErr bool + }{ + { + name: "Get the necessary projects", + currentProjects: []v1.Project{ + { + Name: "project1", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{}, + wantProjects: []string{"project1", "project2"}, + wantErr: false, + }, + { + name: "Get the filtered projects", + currentProjects: []v1.Project{ + { + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ClonePath: "/project", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ClonePath: "/project", + ProjectSource: v1.ProjectSource{ + Zip: &v1.ZipProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + }, + ProjectOptions: common.ProjectOptions{ + ProjectSourceType: v1.GitProjectSourceType, + }, + }, + wantProjects: []string{"project1"}, + wantErr: false, + }, + { + name: "Get the wrong filtered projects", + currentProjects: []v1.Project{ + { + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ClonePath: "/project", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ClonePath: "/project", + ProjectSource: v1.ProjectSource{ + Zip: &v1.ZipProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstStringIsWrong": "firstStringValue", + }, + }, + wantErr: false, + }, + { + name: "Wrong project type", + currentProjects: []v1.Project{ + { + Name: "project1", + ProjectSource: v1.ProjectSource{}, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &DevfileV2{ + v1.Devfile{ + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Projects: tt.currentProjects, + }, + }, + }, + } + + projects, err := d.GetProjects(tt.filterOptions) + if (err != nil) != tt.wantErr { + t.Errorf("TestDevfile200_GetProjects() error = %v, wantErr %v", err, tt.wantErr) + return + } else if err == nil { + assert.Equal(t, len(tt.wantProjects), len(projects), "expected length not the same as returned length") + + for _, devfileProject := range projects { + matched := false + for _, wantProject := range tt.wantProjects { + if wantProject == devfileProject.Name { + matched = true + } + } + + if !matched { + t.Errorf("TestDevfile200_GetProjects() error - project %s not found in the expected list", devfileProject.Name) + } + } + } + }) + } +} + func TestDevfile200_AddProjects(t *testing.T) { currentProject := []v1.Project{ { @@ -246,6 +394,151 @@ func TestDevfile200_DeleteProject(t *testing.T) { } +func TestDevfile200_GetStarterProjects(t *testing.T) { + + tests := []struct { + name string + currentStarterProjects []v1.StarterProject + filterOptions common.DevfileOptions + wantStarterProjects []string + wantErr bool + }{ + { + name: "Get the necessary projects", + currentStarterProjects: []v1.StarterProject{ + { + Name: "project1", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{}, + wantStarterProjects: []string{"project1", "project2"}, + wantErr: false, + }, + { + name: "Get the filtered starter projects", + currentStarterProjects: []v1.StarterProject{ + { + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ProjectSource: v1.ProjectSource{ + Zip: &v1.ZipProjectSource{}, + }, + }, + { + Name: "project3", + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + ProjectOptions: common.ProjectOptions{ + ProjectSourceType: v1.GitProjectSourceType, + }, + }, + wantStarterProjects: []string{"project1", "project3"}, + wantErr: false, + }, + { + name: "Get the wrong filtered starter projects", + currentStarterProjects: []v1.StarterProject{ + { + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ProjectSource: v1.ProjectSource{ + Git: &v1.GitProjectSource{}, + }, + }, + { + Name: "project2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + ProjectSource: v1.ProjectSource{ + Zip: &v1.ZipProjectSource{}, + }, + }, + }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstStringIsWrong": "firstStringValue", + }, + }, + wantErr: false, + }, + { + name: "Wrong starter project type", + currentStarterProjects: []v1.StarterProject{ + { + Name: "project1", + ProjectSource: v1.ProjectSource{}, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := &DevfileV2{ + v1.Devfile{ + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + StarterProjects: tt.currentStarterProjects, + }, + }, + }, + } + + projects, err := d.GetStarterProjects(tt.filterOptions) + if (err != nil) != tt.wantErr { + t.Errorf("TestDevfile200_GetStarterProjects() error = %v, wantErr %v", err, tt.wantErr) + return + } else if err == nil { + assert.Equal(t, len(tt.wantStarterProjects), len(projects), "expected length not the same as returned length") + + for _, devfileProject := range projects { + matched := false + for _, wantProject := range tt.wantStarterProjects { + if wantProject == devfileProject.Name { + matched = true + } + } + + if !matched { + t.Errorf("TestDevfile200_GetStarterProjects() error - project %s not found in the expected list", devfileProject.Name) + } + } + } + }) + } +} + func TestDevfile200_AddStarterProjects(t *testing.T) { currentProject := []v1.StarterProject{ { From 9b136b9484251b79dc37971bdec00082243caa15 Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Wed, 17 Mar 2021 16:26:07 -0400 Subject: [PATCH 4/5] Address DevfileOptions feedback Signed-off-by: Maysun J Faisal --- pkg/devfile/parser/data/v2/commands.go | 6 ++++- pkg/devfile/parser/data/v2/commands_test.go | 10 +++++++- .../parser/data/v2/common/project_helper.go | 2 +- pkg/devfile/parser/data/v2/components.go | 12 ++++++++-- pkg/devfile/parser/data/v2/components_test.go | 10 +++++++- pkg/devfile/parser/data/v2/projects.go | 13 ++++++++-- pkg/devfile/parser/data/v2/projects_test.go | 24 +++++++++++++++---- 7 files changed, 65 insertions(+), 12 deletions(-) diff --git a/pkg/devfile/parser/data/v2/commands.go b/pkg/devfile/parser/data/v2/commands.go index 48ded785..5a9f36cf 100644 --- a/pkg/devfile/parser/data/v2/commands.go +++ b/pkg/devfile/parser/data/v2/commands.go @@ -1,6 +1,7 @@ package v2 import ( + "reflect" "strings" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -10,6 +11,10 @@ import ( // GetCommands returns the slice of Command objects parsed from the Devfile func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, error) { + if reflect.DeepEqual(options, common.DevfileOptions{}) { + return d.Commands, nil + } + var commands []v1.Command for _, command := range d.Commands { // Filter Command Attributes @@ -37,7 +42,6 @@ func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, er continue } - command.Id = strings.ToLower(command.Id) commands = append(commands, command) } diff --git a/pkg/devfile/parser/data/v2/commands_test.go b/pkg/devfile/parser/data/v2/commands_test.go index 9cbc0951..fc7679a1 100644 --- a/pkg/devfile/parser/data/v2/commands_test.go +++ b/pkg/devfile/parser/data/v2/commands_test.go @@ -153,10 +153,18 @@ func TestDevfile200_GetCommands(t *testing.T) { name: "Wrong command type", currentCommands: []v1.Command{ { - Id: "command1", + Id: "command1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + }), CommandUnion: v1.CommandUnion{}, }, }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + }, + }, wantErr: true, }, } diff --git a/pkg/devfile/parser/data/v2/common/project_helper.go b/pkg/devfile/parser/data/v2/common/project_helper.go index 7d755af1..fe2d0859 100644 --- a/pkg/devfile/parser/data/v2/common/project_helper.go +++ b/pkg/devfile/parser/data/v2/common/project_helper.go @@ -42,7 +42,7 @@ func GetDefaultSource(ps v1.GitLikeProjectSource) (remoteName string, remoteURL } -// GetProjectSourceType returns the source type of a given project +// GetProjectSourceType returns the source type of a given project source func GetProjectSourceType(projectSrc v1.ProjectSource) (v1.ProjectSourceType, error) { switch { case projectSrc.Git != nil: diff --git a/pkg/devfile/parser/data/v2/components.go b/pkg/devfile/parser/data/v2/components.go index fdd924d4..372ba80f 100644 --- a/pkg/devfile/parser/data/v2/components.go +++ b/pkg/devfile/parser/data/v2/components.go @@ -1,6 +1,8 @@ package v2 import ( + "reflect" + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" "github.com/devfile/library/pkg/devfile/parser/data/v2/common" ) @@ -8,6 +10,10 @@ import ( // GetComponents returns the slice of Component objects parsed from the Devfile func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component, error) { + if reflect.DeepEqual(options, common.DevfileOptions{}) { + return d.Components, nil + } + var components []v1.Component for _, component := range d.Components { // Filter Component Attributes @@ -33,7 +39,8 @@ func (d *DevfileV2) GetComponents(options common.DevfileOptions) ([]v1.Component return components, nil } -// GetDevfileContainerComponents iterates through the components in the devfile and returns a list of devfile container components. Deprecated, use GetComponents() with the DevfileOptions. +// GetDevfileContainerComponents iterates through the components in the devfile and returns a list of devfile container components. +// Deprecated, use GetComponents() with the DevfileOptions. func (d *DevfileV2) GetDevfileContainerComponents(options common.DevfileOptions) ([]v1.Component, error) { var components []v1.Component devfileComponents, err := d.GetComponents(options) @@ -48,7 +55,8 @@ func (d *DevfileV2) GetDevfileContainerComponents(options common.DevfileOptions) return components, nil } -// GetDevfileVolumeComponents iterates through the components in the devfile and returns a list of devfile volume components. Deprecated, use GetComponents() with the DevfileOptions. +// GetDevfileVolumeComponents iterates through the components in the devfile and returns a list of devfile volume components. +// Deprecated, use GetComponents() with the DevfileOptions. func (d *DevfileV2) GetDevfileVolumeComponents(options common.DevfileOptions) ([]v1.Component, error) { var components []v1.Component devfileComponents, err := d.GetComponents(options) diff --git a/pkg/devfile/parser/data/v2/components_test.go b/pkg/devfile/parser/data/v2/components_test.go index 33ad90d1..02e1a753 100644 --- a/pkg/devfile/parser/data/v2/components_test.go +++ b/pkg/devfile/parser/data/v2/components_test.go @@ -278,10 +278,18 @@ func TestGetDevfileComponents(t *testing.T) { name: "Wrong component type", component: []v1.Component{ { - Name: "comp1", + Name: "comp1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + }), ComponentUnion: v1.ComponentUnion{}, }, }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + }, + }, wantErr: true, }, } diff --git a/pkg/devfile/parser/data/v2/projects.go b/pkg/devfile/parser/data/v2/projects.go index bdfffe9b..20871c98 100644 --- a/pkg/devfile/parser/data/v2/projects.go +++ b/pkg/devfile/parser/data/v2/projects.go @@ -1,6 +1,7 @@ package v2 import ( + "reflect" "strings" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -9,8 +10,12 @@ import ( // GetProjects returns the Project Object parsed from devfile func (d *DevfileV2) GetProjects(options common.DevfileOptions) ([]v1.Project, error) { - var projects []v1.Project + if reflect.DeepEqual(options, common.DevfileOptions{}) { + return d.Projects, nil + } + + var projects []v1.Project for _, project := range d.Projects { // Filter Project Attributes filterIn, err := common.FilterDevfileObject(project.Attributes, options) @@ -80,8 +85,12 @@ func (d *DevfileV2) DeleteProject(name string) error { //GetStarterProjects returns the DevfileStarterProject parsed from devfile func (d *DevfileV2) GetStarterProjects(options common.DevfileOptions) ([]v1.StarterProject, error) { - var starterProjects []v1.StarterProject + if reflect.DeepEqual(options, common.DevfileOptions{}) { + return d.StarterProjects, nil + } + + var starterProjects []v1.StarterProject for _, starterProject := range d.StarterProjects { // Filter Starter Project Attributes filterIn, err := common.FilterDevfileObject(starterProject.Attributes, options) diff --git a/pkg/devfile/parser/data/v2/projects_test.go b/pkg/devfile/parser/data/v2/projects_test.go index 1c5315d9..e15330d5 100644 --- a/pkg/devfile/parser/data/v2/projects_test.go +++ b/pkg/devfile/parser/data/v2/projects_test.go @@ -111,13 +111,21 @@ func TestDevfile200_GetProjects(t *testing.T) { wantErr: false, }, { - name: "Wrong project type", + name: "Wrong project src type", currentProjects: []v1.Project{ { - Name: "project1", + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + }), ProjectSource: v1.ProjectSource{}, }, }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + }, + }, wantErr: true, }, } @@ -493,13 +501,21 @@ func TestDevfile200_GetStarterProjects(t *testing.T) { wantErr: false, }, { - name: "Wrong starter project type", + name: "Wrong starter project src type", currentStarterProjects: []v1.StarterProject{ { - Name: "project1", + Name: "project1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + }), ProjectSource: v1.ProjectSource{}, }, }, + filterOptions: common.DevfileOptions{ + Filter: map[string]interface{}{ + "firstString": "firstStringValue", + }, + }, wantErr: true, }, } From 55a1b83a384bff8a39b1cb4e775ff8674aa0702a Mon Sep 17 00:00:00 2001 From: Maysun J Faisal Date: Thu, 18 Mar 2021 13:49:52 -0400 Subject: [PATCH 5/5] Update Feedback for test 2 Signed-off-by: Maysun J Faisal --- pkg/devfile/parser/data/v2/commands.go | 7 +-- pkg/devfile/parser/data/v2/commands_test.go | 45 +++++++++++++----- pkg/devfile/parser/data/v2/components_test.go | 46 ++++++++++++++---- pkg/devfile/parser/data/v2/projects_test.go | 47 +++++++++++-------- 4 files changed, 103 insertions(+), 42 deletions(-) diff --git a/pkg/devfile/parser/data/v2/commands.go b/pkg/devfile/parser/data/v2/commands.go index 5a9f36cf..9ebb735f 100644 --- a/pkg/devfile/parser/data/v2/commands.go +++ b/pkg/devfile/parser/data/v2/commands.go @@ -36,9 +36,10 @@ func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, er // Filter Command Group Kind - Run, Build, etc. commandGroup := common.GetGroup(command) - if commandGroup != nil && options.CommandOptions.CommandGroupKind != "" && options.CommandOptions.CommandGroupKind != commandGroup.Kind { - continue - } else if commandGroup == nil && options.CommandOptions.CommandGroupKind != "" { + // exclude conditions: + // 1. options group is present and command group is present but does not match + // 2. options group is present and command group is not present + if options.CommandOptions.CommandGroupKind != "" && ((commandGroup != nil && options.CommandOptions.CommandGroupKind != commandGroup.Kind) || commandGroup == nil) { continue } diff --git a/pkg/devfile/parser/data/v2/commands_test.go b/pkg/devfile/parser/data/v2/commands_test.go index fc7679a1..b721ed30 100644 --- a/pkg/devfile/parser/data/v2/commands_test.go +++ b/pkg/devfile/parser/data/v2/commands_test.go @@ -20,7 +20,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr bool }{ { - name: "Get the necessary commands", + name: "Get all the commands", currentCommands: []v1.Command{ { Id: "command1", @@ -35,9 +35,8 @@ func TestDevfile200_GetCommands(t *testing.T) { }, }, }, - filterOptions: common.DevfileOptions{}, - wantCommands: []string{"command1", "command2"}, - wantErr: false, + wantCommands: []string{"command1", "command2"}, + wantErr: false, }, { name: "Get the filtered commands", @@ -105,6 +104,24 @@ func TestDevfile200_GetCommands(t *testing.T) { }, }, }, + { + Id: "command5", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "thirdString": "thirdStringValue", + }), + CommandUnion: v1.CommandUnion{ + Composite: &v1.CompositeCommand{ + LabeledCommand: v1.LabeledCommand{ + BaseCommand: v1.BaseCommand{ + Group: &v1.CommandGroup{ + Kind: v1.RunCommandGroupKind, + }, + }, + }, + }, + }, + }, }, filterOptions: common.DevfileOptions{ Filter: map[string]interface{}{ @@ -119,7 +136,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr: false, }, { - name: "Get the wrong filtered commands", + name: "Wrong filter for commands", currentCommands: []v1.Command{ { Id: "command1", @@ -150,7 +167,7 @@ func TestDevfile200_GetCommands(t *testing.T) { wantErr: false, }, { - name: "Wrong command type", + name: "Invalid command type", currentCommands: []v1.Command{ { Id: "command1", @@ -183,20 +200,24 @@ func TestDevfile200_GetCommands(t *testing.T) { commands, err := d.GetCommands(tt.filterOptions) if (err != nil) != tt.wantErr { t.Errorf("TestDevfile200_GetCommands() error = %v, wantErr %v", err, tt.wantErr) - return } else if err == nil { - assert.Equal(t, len(tt.wantCommands), len(commands), "expected length not the same as returned length") + // confirm the length of actual vs expected + if len(commands) != len(tt.wantCommands) { + t.Errorf("TestDevfile200_GetCommands() error - length of expected commands is not the same as the length of actual commands") + return + } - for _, devfileCommand := range commands { + // compare the command slices for content + for _, wantCommand := range tt.wantCommands { matched := false - for _, wantCommand := range tt.wantCommands { - if wantCommand == devfileCommand.Id { + for _, command := range commands { + if wantCommand == command.Id { matched = true } } if !matched { - t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the expected list", devfileCommand.Id) + t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand) } } } diff --git a/pkg/devfile/parser/data/v2/components_test.go b/pkg/devfile/parser/data/v2/components_test.go index 02e1a753..2d62bc3d 100644 --- a/pkg/devfile/parser/data/v2/components_test.go +++ b/pkg/devfile/parser/data/v2/components_test.go @@ -183,6 +183,32 @@ func TestGetDevfileComponents(t *testing.T) { name: "Invalid devfile", component: []v1.Component{}, }, + { + name: "Get all the components", + component: []v1.Component{ + { + Name: "comp1", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "secondString": "secondStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{}, + }, + }, + { + Name: "comp2", + Attributes: attributes.Attributes{}.FromStringMap(map[string]string{ + "firstString": "firstStringValue", + "fourthString": "fourthStringValue", + }), + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{}, + }, + }, + }, + wantComponents: []string{"comp1", "comp2"}, + }, { name: "Get component with the specified filter", component: []v1.Component{ @@ -241,7 +267,7 @@ func TestGetDevfileComponents(t *testing.T) { wantComponents: []string{"comp3"}, }, { - name: "Get component with the wrong specified filter", + name: "Wrong filter for component", component: []v1.Component{ { Name: "comp1", @@ -275,7 +301,7 @@ func TestGetDevfileComponents(t *testing.T) { wantErr: false, }, { - name: "Wrong component type", + name: "Invalid component type", component: []v1.Component{ { Name: "comp1", @@ -308,20 +334,24 @@ func TestGetDevfileComponents(t *testing.T) { components, err := d.GetComponents(tt.filterOptions) if (err != nil) != tt.wantErr { t.Errorf("TestGetDevfileComponents() error = %v, wantErr %v", err, tt.wantErr) - return } else if err == nil { - assert.Equal(t, len(tt.wantComponents), len(components), "expected length not the same as returned length") + // confirm the length of actual vs expected + if len(components) != len(tt.wantComponents) { + t.Errorf("TestGetDevfileComponents() error - length of expected components is not the same as the length of actual components") + return + } - for _, devfileComponent := range components { + // compare the component slices for content + for _, wantComponent := range tt.wantComponents { matched := false - for _, wantComponent := range tt.wantComponents { - if wantComponent == devfileComponent.Name { + for _, component := range components { + if wantComponent == component.Name { matched = true } } if !matched { - t.Errorf("TestGetDevfileComponents() error - component %s not found in the expected list", devfileComponent.Name) + t.Errorf("TestGetDevfileComponents() error - component %s not found in the devfile", wantComponent) } } } diff --git a/pkg/devfile/parser/data/v2/projects_test.go b/pkg/devfile/parser/data/v2/projects_test.go index e15330d5..6b3e9e2c 100644 --- a/pkg/devfile/parser/data/v2/projects_test.go +++ b/pkg/devfile/parser/data/v2/projects_test.go @@ -21,7 +21,7 @@ func TestDevfile200_GetProjects(t *testing.T) { wantErr bool }{ { - name: "Get the necessary projects", + name: "Get all the projects", currentProjects: []v1.Project{ { Name: "project1", @@ -78,7 +78,7 @@ func TestDevfile200_GetProjects(t *testing.T) { wantErr: false, }, { - name: "Get the wrong filtered projects", + name: "Wrong filter for projects", currentProjects: []v1.Project{ { Name: "project1", @@ -111,7 +111,7 @@ func TestDevfile200_GetProjects(t *testing.T) { wantErr: false, }, { - name: "Wrong project src type", + name: "Invalid project src type", currentProjects: []v1.Project{ { Name: "project1", @@ -144,20 +144,24 @@ func TestDevfile200_GetProjects(t *testing.T) { projects, err := d.GetProjects(tt.filterOptions) if (err != nil) != tt.wantErr { t.Errorf("TestDevfile200_GetProjects() error = %v, wantErr %v", err, tt.wantErr) - return } else if err == nil { - assert.Equal(t, len(tt.wantProjects), len(projects), "expected length not the same as returned length") + // confirm the length of actual vs expected + if len(projects) != len(tt.wantProjects) { + t.Errorf("TestDevfile200_GetProjects() error - length of expected projects is not the same as the length of actual projects") + return + } - for _, devfileProject := range projects { + // compare the project slices for content + for _, wantProject := range tt.wantProjects { matched := false - for _, wantProject := range tt.wantProjects { - if wantProject == devfileProject.Name { + for _, project := range projects { + if wantProject == project.Name { matched = true } } if !matched { - t.Errorf("TestDevfile200_GetProjects() error - project %s not found in the expected list", devfileProject.Name) + t.Errorf("TestDevfile200_GetProjects() error - project %s not found in the devfile", wantProject) } } } @@ -412,7 +416,7 @@ func TestDevfile200_GetStarterProjects(t *testing.T) { wantErr bool }{ { - name: "Get the necessary projects", + name: "Get all the starter projects", currentStarterProjects: []v1.StarterProject{ { Name: "project1", @@ -470,7 +474,7 @@ func TestDevfile200_GetStarterProjects(t *testing.T) { wantErr: false, }, { - name: "Get the wrong filtered starter projects", + name: "Wrong filter for starter projects", currentStarterProjects: []v1.StarterProject{ { Name: "project1", @@ -501,7 +505,7 @@ func TestDevfile200_GetStarterProjects(t *testing.T) { wantErr: false, }, { - name: "Wrong starter project src type", + name: "Invalid starter project src type", currentStarterProjects: []v1.StarterProject{ { Name: "project1", @@ -531,23 +535,28 @@ func TestDevfile200_GetStarterProjects(t *testing.T) { }, } - projects, err := d.GetStarterProjects(tt.filterOptions) + starterProjects, err := d.GetStarterProjects(tt.filterOptions) if (err != nil) != tt.wantErr { t.Errorf("TestDevfile200_GetStarterProjects() error = %v, wantErr %v", err, tt.wantErr) - return } else if err == nil { - assert.Equal(t, len(tt.wantStarterProjects), len(projects), "expected length not the same as returned length") + // confirm the length of actual vs expected + if len(starterProjects) != len(tt.wantStarterProjects) { + t.Errorf("TestDevfile200_GetStarterProjects() error - length of expected starter projects is not the same as the length of actual starter projects") + return + } - for _, devfileProject := range projects { + // compare the starter project slices for content + for _, wantProject := range tt.wantStarterProjects { matched := false - for _, wantProject := range tt.wantStarterProjects { - if wantProject == devfileProject.Name { + + for _, starterProject := range starterProjects { + if wantProject == starterProject.Name { matched = true } } if !matched { - t.Errorf("TestDevfile200_GetStarterProjects() error - project %s not found in the expected list", devfileProject.Name) + t.Errorf("TestDevfile200_GetStarterProjects() error - starter project %s not found in the devfile", wantProject) } } }