Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions api/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"encoding/json"
"fmt"
"strings"
)

// GraphQLRequest represents a GraphQL query request
Expand Down Expand Up @@ -46,17 +47,11 @@ func (r *GraphQLResponse) ErrorMessages() string {
if !r.HasErrors() {
return ""
}
if len(r.Errors) == 1 {
return r.Errors[0].Message
}
msg := ""
messages := make([]string, len(r.Errors))
for i, e := range r.Errors {
if i > 0 {
msg += "; "
}
msg += e.Message
messages[i] = e.Message
}
return msg
return strings.Join(messages, "; ")
}

// ExecuteGraphQL executes a GraphQL query
Expand Down Expand Up @@ -290,7 +285,7 @@ func (s *IntrospectionSchema) GetRootTypes() []IntrospectionType {
var result []IntrospectionType
for _, t := range s.Types {
// Skip internal types (start with __)
if len(t.Name) > 0 && t.Name[0:1] != "_" {
if !strings.HasPrefix(t.Name, "__") {
// Include OBJECT and INTERFACE types that have fields
if (t.Kind == "OBJECT" || t.Kind == "INTERFACE") && len(t.Fields) > 0 {
result = append(result, t)
Expand Down
23 changes: 10 additions & 13 deletions internal/cmd/graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import (
"github.com/open-cli-collective/hubspot-cli/internal/cmd/root"
)

// tableRenderer is the interface for rendering tabular output
type tableRenderer interface {
Render([]string, [][]string, interface{}) error
Info(string, ...interface{})
Error(string, ...interface{})
}

// Register registers the graphql command and subcommands
func Register(parent *cobra.Command, opts *root.Options) {
cmd := &cobra.Command{
Expand Down Expand Up @@ -185,10 +192,7 @@ specific type, and --field to show details of a specific field.`,
return cmd
}

func listRootTypes(v interface {
Render([]string, [][]string, interface{}) error
Info(string, ...interface{})
}, schema *api.IntrospectionSchema) error {
func listRootTypes(v tableRenderer, schema *api.IntrospectionSchema) error {
types := schema.GetRootTypes()
if len(types) == 0 {
v.Info("No types found in schema")
Expand All @@ -214,10 +218,7 @@ func listRootTypes(v interface {
return v.Render(headers, rows, types)
}

func showTypeFields(v interface {
Render([]string, [][]string, interface{}) error
Info(string, ...interface{})
}, t *api.IntrospectionType) error {
func showTypeFields(v tableRenderer, t *api.IntrospectionType) error {
if len(t.Fields) == 0 {
v.Info("Type %s has no fields", t.Name)
return nil
Expand Down Expand Up @@ -246,11 +247,7 @@ func showTypeFields(v interface {
return v.Render(headers, rows, t.Fields)
}

func showFieldDetails(v interface {
Render([]string, [][]string, interface{}) error
Info(string, ...interface{})
Error(string, ...interface{})
}, t *api.IntrospectionType, fieldName string) error {
func showFieldDetails(v tableRenderer, t *api.IntrospectionType, fieldName string) error {
var field *api.IntrospectionField
for i := range t.Fields {
if t.Fields[i].Name == fieldName {
Expand Down
26 changes: 17 additions & 9 deletions internal/cmd/schemas/schemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/open-cli-collective/hubspot-cli/api"
"github.com/open-cli-collective/hubspot-cli/internal/cmd/root"
"github.com/open-cli-collective/hubspot-cli/internal/cmd/shared"
)

// Register registers the schemas command and subcommands
Expand Down Expand Up @@ -129,7 +130,7 @@ func newGetCmd(opts *root.Options) *cobra.Command {
{"Singular Label", schema.Labels.Singular},
{"Plural Label", schema.Labels.Plural},
{"Primary Display Property", schema.PrimaryDisplayProperty},
{"Archived", formatBool(schema.Archived)},
{"Archived", shared.FormatBool(schema.Archived)},
{"Created", schema.CreatedAt},
{"Updated", schema.UpdatedAt},
}
Expand Down Expand Up @@ -198,17 +199,27 @@ func newCreateCmd(opts *root.Options) *cobra.Command {
}

func newDeleteCmd(opts *root.Options) *cobra.Command {
return &cobra.Command{
var force bool

cmd := &cobra.Command{
Use: "delete <fullyQualifiedName>",
Short: "Delete a custom object schema",
Long: "Delete a custom object schema by its fully qualified name. This will also delete all objects of that type.",
Example: ` # Delete schema by fully qualified name
hspt schemas delete p_my_custom_object`,
hspt schemas delete p_my_custom_object

# Delete without confirmation
hspt schemas delete p_my_custom_object --force`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
v := opts.View()
fqn := args[0]

if !force {
v.Warning("This will delete schema %s and all objects of that type. Use --force to confirm.", fqn)
return nil
}

client, err := opts.APIClient()
if err != nil {
return err
Expand All @@ -227,11 +238,8 @@ func newDeleteCmd(opts *root.Options) *cobra.Command {
return nil
},
}
}

func formatBool(b bool) string {
if b {
return "Yes"
}
return "No"
cmd.Flags().BoolVar(&force, "force", false, "Confirm deletion without prompt")

return cmd
}
10 changes: 10 additions & 0 deletions internal/cmd/shared/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package shared

// FormatBool returns "Yes" or "No" for boolean values.
// Used for human-readable output in table views.
func FormatBool(b bool) string {
if b {
return "Yes"
}
return "No"
}
23 changes: 23 additions & 0 deletions internal/cmd/shared/format_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package shared

import "testing"

func TestFormatBool(t *testing.T) {
tests := []struct {
name string
input bool
want string
}{
{"true returns Yes", true, "Yes"},
{"false returns No", false, "No"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := FormatBool(tt.input)
if got != tt.want {
t.Errorf("FormatBool(%v) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
30 changes: 19 additions & 11 deletions internal/cmd/workflows/workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/open-cli-collective/hubspot-cli/api"
"github.com/open-cli-collective/hubspot-cli/internal/cmd/root"
"github.com/open-cli-collective/hubspot-cli/internal/cmd/shared"
)

// Register registers the workflows command and subcommands
Expand Down Expand Up @@ -71,7 +72,7 @@ func newListCmd(opts *root.Options) *cobra.Command {
workflow.ID,
workflow.Name,
workflow.Type,
formatBool(workflow.Enabled),
shared.FormatBool(workflow.Enabled),
formatObjectType(workflow.ObjectTypeID),
})
}
Expand Down Expand Up @@ -125,7 +126,7 @@ func newGetCmd(opts *root.Options) *cobra.Command {
{"ID", workflow.ID},
{"Name", workflow.Name},
{"Type", workflow.Type},
{"Enabled", formatBool(workflow.Enabled)},
{"Enabled", shared.FormatBool(workflow.Enabled)},
{"Object Type", formatObjectType(workflow.ObjectTypeID)},
{"Revision ID", workflow.RevisionID},
{"Created", workflow.CreatedAt},
Expand All @@ -137,13 +138,6 @@ func newGetCmd(opts *root.Options) *cobra.Command {
}
}

func formatBool(b bool) string {
if b {
return "Yes"
}
return "No"
}

func formatObjectType(objectTypeID string) string {
// HubSpot standard object type IDs
objectTypes := map[string]string{
Expand Down Expand Up @@ -264,17 +258,27 @@ func newUpdateCmd(opts *root.Options) *cobra.Command {
}

func newDeleteCmd(opts *root.Options) *cobra.Command {
return &cobra.Command{
var force bool

cmd := &cobra.Command{
Use: "delete <id>",
Short: "Delete a workflow",
Long: "Delete an automation workflow by ID.",
Example: ` # Delete a workflow
hspt workflows delete 12345`,
hspt workflows delete 12345

# Delete without confirmation
hspt workflows delete 12345 --force`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
v := opts.View()
id := args[0]

if !force {
v.Warning("This will delete workflow %s. Use --force to confirm.", id)
return nil
}

client, err := opts.APIClient()
if err != nil {
return err
Expand All @@ -293,6 +297,10 @@ func newDeleteCmd(opts *root.Options) *cobra.Command {
return nil
},
}

cmd.Flags().BoolVar(&force, "force", false, "Confirm deletion without prompt")

return cmd
}

func newEnrollCmd(opts *root.Options) *cobra.Command {
Expand Down