fix: [CDE-555]: allow mixed data types in lifecycle command object fo… (#3604)

(#3169)
<img width="1569" alt="Screenshot 2024-12-17 at 2 29 36 PM"
src="https://github.com/user-attachments/assets/e635fc69-3264-4b65-8b4e-6b9df016a21c"
/>

* fix: [CDE-555]: allow mixed data types in lifecycle command object
form.

(cherry picked from commit 9c792e53fd)

[CDE-555]:
https://harness.atlassian.net/browse/CDE-555?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
pull/3610/head v3.1.0
Ansuman Satapathy 2024-12-17 14:57:00 +05:30 committed by GitHub
parent cf8b8b2215
commit 231536844c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 53 additions and 44 deletions

View File

@ -16,7 +16,7 @@ package types
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
@ -33,24 +33,21 @@ type DevcontainerConfig struct {
RemoteUser string `json:"remoteUser,omitempty"`
}
//nolint:tagliatelle
type LifecycleCommand struct {
CommandString string `json:"commandString,omitempty"`
CommandArray []string `json:"commandArray,omitempty"`
// Map to store commands by tags
CommandMap map[string]string `json:"commandMap,omitempty"`
CommandMapArray map[string][]string `json:"commandMapArray,omitempty"`
Discriminator string `json:"-"` // Tracks the original type for proper re-marshaling
}
// Constants for discriminator values.
const (
TypeString = "string"
TypeArray = "array"
TypeCommandMapString = "commandMap"
TypeCommandMapArray = "commandMapArray"
TypeString = "string"
TypeArray = "array"
TypeCommandMap = "commandMap"
)
//nolint:tagliatelle
type LifecycleCommand struct {
CommandString string `json:"commandString,omitempty"`
CommandArray []string `json:"commandArray,omitempty"`
CommandMap map[string]any `json:"commandMap,omitempty"`
Discriminator string `json:"-"` // Tracks the original type for proper re-marshaling
}
func (lc *LifecycleCommand) UnmarshalJSON(data []byte) error {
// Try to unmarshal as a single string
var commandStr string
@ -59,6 +56,7 @@ func (lc *LifecycleCommand) UnmarshalJSON(data []byte) error {
lc.Discriminator = TypeString
return nil
}
// Try to unmarshal as an array of strings
var commandArr []string
if err := json.Unmarshal(data, &commandArr); err == nil {
@ -66,21 +64,35 @@ func (lc *LifecycleCommand) UnmarshalJSON(data []byte) error {
lc.Discriminator = TypeArray
return nil
}
// Try to unmarshal as a map of commands (tags to commands)
var commandMap map[string]string
if err := json.Unmarshal(data, &commandMap); err == nil {
lc.CommandMap = commandMap
lc.Discriminator = TypeCommandMapString
// Try to unmarshal as a map with mixed types
var rawMap map[string]any
if err := json.Unmarshal(data, &rawMap); err == nil {
for key, value := range rawMap {
switch v := value.(type) {
case string:
// Valid string value
case []interface{}:
// Convert []interface{} to []string
var strArray []string
for _, item := range v {
if str, ok := item.(string); ok {
strArray = append(strArray, str)
} else {
return fmt.Errorf("invalid format: array contains non-string value")
}
}
rawMap[key] = strArray
default:
return fmt.Errorf("invalid format: map contains unsupported type")
}
}
lc.CommandMap = rawMap
lc.Discriminator = TypeCommandMap
return nil
}
// Try to unmarshal as a CommandMapArray
var commandMapArray map[string][]string
if err := json.Unmarshal(data, &commandMapArray); err == nil {
lc.CommandMapArray = commandMapArray
lc.Discriminator = TypeCommandMapArray
return nil
}
return errors.New("invalid format: must be string, []string, map[string]string, or map[string][]string")
return fmt.Errorf("invalid format: must be string, []string, or map[string]any")
}
func (lc *LifecycleCommand) MarshalJSON() ([]byte, error) {
@ -89,32 +101,29 @@ func (lc *LifecycleCommand) MarshalJSON() ([]byte, error) {
return json.Marshal(lc.CommandString)
case TypeArray:
return json.Marshal(lc.CommandArray)
case TypeCommandMapString:
case TypeCommandMap:
return json.Marshal(lc.CommandMap)
case TypeCommandMapArray:
return json.Marshal(lc.CommandMapArray)
default:
return nil, errors.New("unknown type for LifecycleCommand")
return nil, fmt.Errorf("unknown type for LifecycleCommand")
}
}
// ToCommandArray converts the LifecycleCommand into a slice of full commands.
func (lc *LifecycleCommand) ToCommandArray() []string {
switch {
case lc.CommandString != "":
switch lc.Discriminator {
case TypeString:
return []string{lc.CommandString}
case lc.CommandArray != nil:
case TypeArray:
return []string{strings.Join(lc.CommandArray, " ")}
case lc.CommandMap != nil:
case TypeCommandMap:
var commands []string
for _, command := range lc.CommandMap {
commands = append(commands, command)
}
return commands
case lc.CommandMapArray != nil:
var commands []string
for _, commandArray := range lc.CommandMapArray {
commands = append(commands, strings.Join(commandArray, " "))
for _, value := range lc.CommandMap {
switch v := value.(type) {
case string:
commands = append(commands, v)
case []string:
commands = append(commands, strings.Join(v, " "))
}
}
return commands
default: