MergeDevWorkspaceTemplateSpec implements the merging logic of a main devfile content with flattened, already-overridden parent devfiles or plugins. On a `main` `DevWorkspaceTemplateSpec` (which is the core part of a devfile, without the `apiVersion` and `metadata`), it allows adding all the new over
( mainContent *dw.DevWorkspaceTemplateSpecContent, parentFlattenedContent *dw.DevWorkspaceTemplateSpecContent, pluginFlattenedContents ...*dw.DevWorkspaceTemplateSpecContent)
| 38 | // The result is a transformed `DevWorkspaceTemplateSpec` object, that does not contain any `plugin` component |
| 39 | // (since they are expected to be provided as flattened overridden devfiles in the arguments) |
| 40 | func MergeDevWorkspaceTemplateSpec( |
| 41 | mainContent *dw.DevWorkspaceTemplateSpecContent, |
| 42 | parentFlattenedContent *dw.DevWorkspaceTemplateSpecContent, |
| 43 | pluginFlattenedContents ...*dw.DevWorkspaceTemplateSpecContent) (*dw.DevWorkspaceTemplateSpecContent, error) { |
| 44 | |
| 45 | allContents := []*dw.DevWorkspaceTemplateSpecContent{} |
| 46 | if parentFlattenedContent != nil { |
| 47 | allContents = append(allContents, parentFlattenedContent) |
| 48 | } |
| 49 | if len(pluginFlattenedContents) > 0 { |
| 50 | allContents = append(allContents, pluginFlattenedContents...) |
| 51 | } |
| 52 | allContents = append(allContents, mainContent) |
| 53 | |
| 54 | // Check for conflicts |
| 55 | if parentFlattenedContent != nil { |
| 56 | if err := ensureNoConflictWithParent(mainContent, parentFlattenedContent); err != nil { |
| 57 | return nil, err |
| 58 | } |
| 59 | } |
| 60 | if len(pluginFlattenedContents) > 0 { |
| 61 | if err := ensureNoConflictsWithPlugins(mainContent, pluginFlattenedContents...); err != nil { |
| 62 | return nil, err |
| 63 | } |
| 64 | if parentFlattenedContent != nil { |
| 65 | // also need to ensure no conflict between parent and plugins |
| 66 | if err := ensureNoConflictsWithPlugins(parentFlattenedContent, pluginFlattenedContents...); err != nil { |
| 67 | return nil, err |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | result := dw.DevWorkspaceTemplateSpecContent{} |
| 73 | |
| 74 | // Merge top-level lists (Commands, Projects, Components, etc ...) |
| 75 | |
| 76 | topLevelListsNames := result.GetToplevelLists() |
| 77 | topLevelListsByContent := []dw.TopLevelLists{} |
| 78 | for _, content := range allContents { |
| 79 | topLevelListsByContent = append(topLevelListsByContent, content.GetToplevelLists()) |
| 80 | } |
| 81 | |
| 82 | resultValue := reflect.ValueOf(&result).Elem() |
| 83 | for toplevelListName := range topLevelListsNames { |
| 84 | listType, fieldExists := resultValue.Type().FieldByName(toplevelListName) |
| 85 | if !fieldExists { |
| 86 | return nil, fmt.Errorf("field '%v' is unknown in %v struct (this should never happen)", toplevelListName, reflect.TypeOf(resultValue).Name()) |
| 87 | } |
| 88 | if listType.Type.Kind() != reflect.Slice { |
| 89 | return nil, fmt.Errorf("field '%v' in %v struct is not a slice (this should never happen)", toplevelListName, reflect.TypeOf(resultValue).Name()) |
| 90 | } |
| 91 | listElementType := listType.Type.Elem() |
| 92 | resultToplevelListValue := resultValue.FieldByName(toplevelListName) |
| 93 | for contentIndex, content := range allContents { |
| 94 | toplevelLists := topLevelListsByContent[contentIndex] |
| 95 | keyedList := toplevelLists[toplevelListName] |
| 96 | for _, keyed := range keyedList { |
| 97 | if content == mainContent { |