Skip to content

Commit

Permalink
feat: add minimal flags
Browse files Browse the repository at this point in the history
Signed-off-by: Youngjin Jo <youngjinjo@megazone.com>
  • Loading branch information
yjinjo committed Dec 5, 2024
1 parent ac2312a commit bd1d930
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 9 deletions.
142 changes: 133 additions & 9 deletions cmd/common/fetchService.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,40 @@ func FetchService(serviceName string, verb string, resourceName string, options
return nil, nil
}

// Get hostPort based on environment prefix
var envPrefix string
if strings.HasPrefix(config.Environment, "dev-") {
envPrefix = "dev"
} else if strings.HasPrefix(config.Environment, "stg-") {
envPrefix = "stg"
}
hostPort := fmt.Sprintf("%s.api.%s.spaceone.dev:443", serviceName, envPrefix)

// Configure gRPC connection
tlsConfig := &tls.Config{
InsecureSkipVerify: false,
}
creds := credentials.NewTLS(tlsConfig)
opts := []grpc.DialOption{
grpc.WithTransportCredentials(creds),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(10*1024*1024), // 10MB
grpc.MaxCallSendMsgSize(10*1024*1024), // 10MB
),
}

// Establish the connection
conn, err := grpc.Dial(hostPort, opts...)
if err != nil {
return nil, fmt.Errorf("connection failed: unable to connect to %s: %v", hostPort, err)
}
defer conn.Close()

// Create reflection client for both service calls and minimal fields detection
ctx := metadata.AppendToOutgoingContext(context.Background(), "token", config.Environments[config.Environment].Token)
refClient := grpcreflect.NewClient(ctx, grpc_reflection_v1alpha.NewServerReflectionClient(conn))
defer refClient.Reset()

// Call the service
jsonBytes, err := fetchJSONResponse(config, serviceName, verb, resourceName, options)
if err != nil {
Expand All @@ -175,14 +209,14 @@ func FetchService(serviceName string, verb string, resourceName string, options
options.Parameters = append(options.Parameters, fmt.Sprintf("%s=%s", paramName, value))

// Retry the call with the new parameter
jsonBytes, err := fetchJSONResponse(config, serviceName, verb, resourceName, options)
jsonBytes, err = fetchJSONResponse(config, serviceName, verb, resourceName, options)
if err != nil {
return nil, err
}

// Unmarshal JSON bytes to a map
var respMap map[string]interface{}
if err := json.Unmarshal(jsonBytes, &respMap); err != nil {
if err = json.Unmarshal(jsonBytes, &respMap); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

Expand Down Expand Up @@ -222,7 +256,7 @@ func FetchService(serviceName string, verb string, resourceName string, options
respMap["results"] = results
}
}
printData(respMap, options)
printData(respMap, options, serviceName, resourceName, refClient)
}

return respMap, nil
Expand All @@ -233,7 +267,7 @@ func FetchService(serviceName string, verb string, resourceName string, options

// Unmarshal JSON bytes to a map
var respMap map[string]interface{}
if err := json.Unmarshal(jsonBytes, &respMap); err != nil {
if err = json.Unmarshal(jsonBytes, &respMap); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %v", err)
}

Expand Down Expand Up @@ -273,7 +307,7 @@ func FetchService(serviceName string, verb string, resourceName string, options
respMap["results"] = results
}
}
printData(respMap, options)
printData(respMap, options, serviceName, resourceName, refClient)
}

return respMap, nil
Expand Down Expand Up @@ -495,7 +529,7 @@ func discoverService(refClient *grpcreflect.Client, serviceName string, resource
return "", fmt.Errorf("service not found for %s.%s", serviceName, resourceName)
}

func printData(data map[string]interface{}, options *FetchOptions) {
func printData(data map[string]interface{}, options *FetchOptions, serviceName, resourceName string, refClient *grpcreflect.Client) {
var output string

switch options.OutputFormat {
Expand All @@ -521,13 +555,13 @@ func printData(data map[string]interface{}, options *FetchOptions) {
case "table":
// Check if data has 'results' key
if _, ok := data["results"].([]interface{}); ok {
output = printTable(data)
output = printTable(data, options, serviceName, resourceName, refClient)
} else {
// If no 'results' key, treat the entire data as results
wrappedData := map[string]interface{}{
"results": []interface{}{data},
}
output = printTable(wrappedData)
output = printTable(wrappedData, options, serviceName, resourceName, refClient)
}

case "csv":
Expand All @@ -554,7 +588,83 @@ func printData(data map[string]interface{}, options *FetchOptions) {
}
}

func printTable(data map[string]interface{}) string {
func getMinimalFields(serviceName, resourceName string, refClient *grpcreflect.Client) []string {
// Default minimal fields that should always be included if they exist
defaultFields := []string{"name", "created_at"}

// Try to get message descriptor for the resource
fullServiceName := fmt.Sprintf("spaceone.api.%s.v1.%s", serviceName, resourceName)
serviceDesc, err := refClient.ResolveService(fullServiceName)
if err != nil {
// Try v2 if v1 fails
fullServiceName = fmt.Sprintf("spaceone.api.%s.v2.%s", serviceName, resourceName)
serviceDesc, err = refClient.ResolveService(fullServiceName)
if err != nil {
return defaultFields
}
}

// Get list method descriptor
listMethod := serviceDesc.FindMethodByName("list")
if listMethod == nil {
return defaultFields
}

// Get response message descriptor
respDesc := listMethod.GetOutputType()
if respDesc == nil {
return defaultFields
}

// Find the 'results' field which should be repeated message type
resultsField := respDesc.FindFieldByName("results")
if resultsField == nil {
return defaultFields
}

// Get the message type of items in the results
itemMsgDesc := resultsField.GetMessageType()
if itemMsgDesc == nil {
return defaultFields
}

// Collect required fields and important fields
minimalFields := make([]string, 0)
fields := itemMsgDesc.GetFields()
for _, field := range fields {
// Add ID fields
if strings.HasSuffix(field.GetName(), "_id") {
minimalFields = append(minimalFields, field.GetName())
continue
}

// Add status/state fields
if field.GetName() == "status" || field.GetName() == "state" {
minimalFields = append(minimalFields, field.GetName())
continue
}

// Add timestamp fields
if field.GetName() == "created_at" || field.GetName() == "finished_at" {
minimalFields = append(minimalFields, field.GetName())
continue
}

// Add name field
if field.GetName() == "name" {
minimalFields = append(minimalFields, field.GetName())
continue
}
}

if len(minimalFields) == 0 {
return defaultFields
}

return minimalFields
}

func printTable(data map[string]interface{}, options *FetchOptions, serviceName, resourceName string, refClient *grpcreflect.Client) string {
if results, ok := data["results"].([]interface{}); ok {
pageSize := 10
currentPage := 0
Expand Down Expand Up @@ -585,6 +695,20 @@ func printTable(data map[string]interface{}) string {
}
sort.Strings(headerSlice)

// If minimal columns are requested, only show essential fields
if options.MinimalColumns {
minimalFields := getMinimalFields(serviceName, resourceName, refClient)
var minimalHeaderSlice []string
for _, field := range minimalFields {
if headers[field] {
minimalHeaderSlice = append(minimalHeaderSlice, field)
}
}
if len(minimalHeaderSlice) > 0 {
headerSlice = minimalHeaderSlice
}
}

for {
if searchTerm != "" {
filteredResults = filterResults(results, searchTerm)
Expand Down
3 changes: 3 additions & 0 deletions cmd/common/fetchVerb.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type FetchOptions struct {
OutputFormat string
CopyToClipboard bool
SortBy string
MinimalColumns bool
}

// AddVerbCommands adds subcommands for each verb to the parent command
Expand Down Expand Up @@ -124,6 +125,7 @@ func AddVerbCommands(parentCmd *cobra.Command, serviceName string, groupID strin
OutputFormat: outputFormat,
CopyToClipboard: copyToClipboard,
SortBy: sortBy,
MinimalColumns: cmd.Flag("minimal").Changed,
}

if currentVerb == "list" && !cmd.Flags().Changed("output") {
Expand Down Expand Up @@ -152,6 +154,7 @@ func AddVerbCommands(parentCmd *cobra.Command, serviceName string, groupID strin
if currentVerb == "list" {
verbCmd.Flags().BoolP("watch", "w", false, "Watch for changes")
verbCmd.Flags().StringP("sort", "s", "", "Sort by field (e.g. 'name', 'created_at')")
verbCmd.Flags().BoolP("minimal", "m", false, "Show minimal columns")
}

// Define flags for verbCmd
Expand Down

0 comments on commit bd1d930

Please sign in to comment.