Skip to content

Commit ac8fff0

Browse files
raulblovromazgon
andauthored
feat(CLI): add connectors describe (#2082)
* fixes * move common print utils * utilize utility functions * describe a connector * fix lint * Update cmd/conduit/root/pipelines/describe.go Co-authored-by: Lovro Mažgon <[email protected]> * Update cmd/conduit/root/connectors/describe.go Co-authored-by: Lovro Mažgon <[email protected]> * Update cmd/conduit/internal/print_utils.go Co-authored-by: Lovro Mažgon <[email protected]> * display processors on pipelines desc and sort * add some tests * lint --------- Co-authored-by: Lovro Mažgon <[email protected]>
1 parent 37457e2 commit ac8fff0

File tree

12 files changed

+415
-95
lines changed

12 files changed

+415
-95
lines changed

Diff for: cmd/conduit/internal/print_utils.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright © 2025 Meroxa, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package internal
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
21+
apiv1 "github.com/conduitio/conduit/proto/api/v1"
22+
"google.golang.org/protobuf/types/known/timestamppb"
23+
)
24+
25+
// Indentation returns a string with the number of spaces equal to the level
26+
func Indentation(level int) string {
27+
return strings.Repeat(" ", level)
28+
}
29+
30+
// PrintStatusFromProtoString returns a human-readable status from a proto status
31+
func PrintStatusFromProtoString(protoStatus string) string {
32+
return PrettyProtoEnum("STATUS_", protoStatus)
33+
}
34+
35+
// PrettyProtoEnum returns a human-readable string from a proto enum
36+
func PrettyProtoEnum(prefix, protoEnum string) string {
37+
return strings.ToLower(
38+
strings.TrimPrefix(protoEnum, prefix),
39+
)
40+
}
41+
42+
// PrintTime returns a human-readable time from a timestamp
43+
func PrintTime(ts *timestamppb.Timestamp) string {
44+
return ts.AsTime().Format("2006-01-02T15:04:05Z")
45+
}
46+
47+
// DisplayProcessors prints the processors in a human-readable format
48+
func DisplayProcessors(processors []*apiv1.Processor, indent int) {
49+
if len(processors) == 0 {
50+
return
51+
}
52+
53+
fmt.Printf("%sProcessors:\n", Indentation(indent))
54+
55+
for _, p := range processors {
56+
fmt.Printf("%s- ID: %s\n", Indentation(indent+1), p.Id)
57+
fmt.Printf("%sPlugin: %s\n", Indentation(indent+2), p.Plugin)
58+
59+
if p.Condition != "" {
60+
fmt.Printf("%sCondition: %s\n", Indentation(indent+2), p.Condition)
61+
}
62+
63+
fmt.Printf("%sConfig:\n", Indentation(indent+2))
64+
for name, value := range p.Config.Settings {
65+
fmt.Printf("%s%s: %s\n", Indentation(indent+3), name, value)
66+
}
67+
fmt.Printf("%sWorkers: %d\n", Indentation(indent+3), p.Config.Workers)
68+
69+
fmt.Printf("%sCreated At: %s\n", Indentation(indent+2), PrintTime(p.CreatedAt))
70+
fmt.Printf("%sUpdated At: %s\n", Indentation(indent+2), PrintTime(p.UpdatedAt))
71+
}
72+
}

Diff for: cmd/conduit/root/connectorplugins/list.go

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package connectorplugins
1717
import (
1818
"context"
1919
"fmt"
20+
"sort"
2021

2122
"github.com/alexeyco/simpletable"
2223
"github.com/conduitio/conduit/cmd/conduit/api"
@@ -66,6 +67,10 @@ func (c *ListCommand) ExecuteWithClient(ctx context.Context, client *api.Client)
6667
return fmt.Errorf("failed to list connector plugins: %w", err)
6768
}
6869

70+
sort.Slice(resp.Plugins, func(i, j int) bool {
71+
return resp.Plugins[i].Name < resp.Plugins[j].Name
72+
})
73+
6974
displayConnectorPlugins(resp.Plugins)
7075

7176
return nil

Diff for: cmd/conduit/root/connectors/connectors.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func (c *ConnectorsCommand) Aliases() []string { return []string{"connector"} }
3131
func (c *ConnectorsCommand) SubCommands() []ecdysis.Command {
3232
return []ecdysis.Command{
3333
&ListCommand{},
34+
&DescribeCommand{},
3435
}
3536
}
3637

Diff for: cmd/conduit/root/connectors/describe.go

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright © 2025 Meroxa, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package connectors
16+
17+
import (
18+
"context"
19+
"fmt"
20+
21+
"github.com/conduitio/conduit/cmd/conduit/api"
22+
"github.com/conduitio/conduit/cmd/conduit/cecdysis"
23+
"github.com/conduitio/conduit/cmd/conduit/internal"
24+
"github.com/conduitio/conduit/pkg/foundation/cerrors"
25+
apiv1 "github.com/conduitio/conduit/proto/api/v1"
26+
"github.com/conduitio/ecdysis"
27+
)
28+
29+
var (
30+
_ cecdysis.CommandWithExecuteWithClient = (*DescribeCommand)(nil)
31+
_ ecdysis.CommandWithAliases = (*DescribeCommand)(nil)
32+
_ ecdysis.CommandWithDocs = (*DescribeCommand)(nil)
33+
_ ecdysis.CommandWithArgs = (*DescribeCommand)(nil)
34+
)
35+
36+
type DescribeArgs struct {
37+
ConnectorID string
38+
}
39+
40+
type DescribeCommand struct {
41+
args DescribeArgs
42+
}
43+
44+
func (c *DescribeCommand) Usage() string { return "describe" }
45+
46+
func (c *DescribeCommand) Docs() ecdysis.Docs {
47+
return ecdysis.Docs{
48+
Short: "Describe an existing connector",
49+
Long: `This command requires Conduit to be already running since it will describe a connector registered
50+
by Conduit. You can list existing connectors with the 'conduit connectors list' command.`,
51+
Example: "conduit connectors describe connector:source\n" +
52+
"conduit connectors desc connector:destination",
53+
}
54+
}
55+
56+
func (c *DescribeCommand) Aliases() []string { return []string{"desc"} }
57+
58+
func (c *DescribeCommand) Args(args []string) error {
59+
if len(args) == 0 {
60+
return cerrors.Errorf("requires a connector ID")
61+
}
62+
63+
if len(args) > 1 {
64+
return cerrors.Errorf("too many arguments")
65+
}
66+
67+
c.args.ConnectorID = args[0]
68+
return nil
69+
}
70+
71+
func (c *DescribeCommand) ExecuteWithClient(ctx context.Context, client *api.Client) error {
72+
resp, err := client.ConnectorServiceClient.GetConnector(ctx, &apiv1.GetConnectorRequest{
73+
Id: c.args.ConnectorID,
74+
})
75+
if err != nil {
76+
return fmt.Errorf("failed to get connector: %w", err)
77+
}
78+
79+
var processors []*apiv1.Processor
80+
81+
for _, processorID := range resp.Connector.ProcessorIds {
82+
processor, err := client.ProcessorServiceClient.GetProcessor(ctx, &apiv1.GetProcessorRequest{
83+
Id: processorID,
84+
})
85+
if err != nil {
86+
return fmt.Errorf("failed to get processor: %w", err)
87+
}
88+
processors = append(processors, processor.Processor)
89+
}
90+
91+
displayConnector(resp.Connector, processors)
92+
93+
return nil
94+
}
95+
96+
func displayConnector(connector *apiv1.Connector, processors []*apiv1.Processor) {
97+
if connector == nil {
98+
return
99+
}
100+
101+
fmt.Printf("ID: %s\n", connector.Id)
102+
103+
var connectorType string
104+
105+
switch connector.Type {
106+
case apiv1.Connector_TYPE_SOURCE:
107+
connectorType = "Source"
108+
case apiv1.Connector_TYPE_DESTINATION:
109+
connectorType = "Destination"
110+
case apiv1.Connector_TYPE_UNSPECIFIED:
111+
connectorType = "Unspecified"
112+
default:
113+
connectorType = "Unknown"
114+
}
115+
116+
fmt.Printf("Type: %s\n", connectorType)
117+
fmt.Printf("Plugin: %s\n", connector.Plugin)
118+
fmt.Printf("Pipeline ID: %s\n", connector.PipelineId)
119+
120+
displayConnectorConfig(connector.Config)
121+
fmt.Printf("Created At: %s\n", internal.PrintTime(connector.CreatedAt))
122+
fmt.Printf("Updated At: %s\n", internal.PrintTime(connector.UpdatedAt))
123+
124+
internal.DisplayProcessors(processors, 0)
125+
}
126+
127+
func displayConnectorConfig(cfg *apiv1.Connector_Config) {
128+
fmt.Println("Config:")
129+
130+
for name, value := range cfg.Settings {
131+
fmt.Printf("%s%s: %s\n", internal.Indentation(1), name, value)
132+
}
133+
}

Diff for: cmd/conduit/root/connectors/describe_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright © 2025 Meroxa, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package connectors
16+
17+
import (
18+
"testing"
19+
20+
"github.com/matryer/is"
21+
)
22+
23+
func TestDescribeExecutionNoArgs(t *testing.T) {
24+
is := is.New(t)
25+
26+
c := DescribeCommand{}
27+
err := c.Args([]string{})
28+
29+
expected := "requires a connector ID"
30+
31+
is.True(err != nil)
32+
is.Equal(err.Error(), expected)
33+
}
34+
35+
func TestDescribeExecutionMultipleArgs(t *testing.T) {
36+
is := is.New(t)
37+
38+
c := DescribeCommand{}
39+
err := c.Args([]string{"foo", "bar"})
40+
41+
expected := "too many arguments"
42+
43+
is.True(err != nil)
44+
is.Equal(err.Error(), expected)
45+
}
46+
47+
func TestDescribeExecutionCorrectArgs(t *testing.T) {
48+
is := is.New(t)
49+
connectorID := "my-connector"
50+
51+
c := DescribeCommand{}
52+
err := c.Args([]string{connectorID})
53+
54+
is.NoErr(err)
55+
is.Equal(c.args.ConnectorID, connectorID)
56+
}

Diff for: cmd/conduit/root/connectors/list.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ package connectors
1717
import (
1818
"context"
1919
"fmt"
20+
"sort"
2021

2122
"github.com/alexeyco/simpletable"
2223
"github.com/conduitio/conduit/cmd/conduit/api"
2324
"github.com/conduitio/conduit/cmd/conduit/cecdysis"
25+
"github.com/conduitio/conduit/cmd/conduit/internal"
2426
apiv1 "github.com/conduitio/conduit/proto/api/v1"
2527
"github.com/conduitio/ecdysis"
2628
)
@@ -65,6 +67,10 @@ func (c *ListCommand) ExecuteWithClient(ctx context.Context, client *api.Client)
6567
return fmt.Errorf("failed to list connectors: %w", err)
6668
}
6769

70+
sort.Slice(resp.Connectors, func(i, j int) bool {
71+
return resp.Connectors[i].Id < resp.Connectors[j].Id
72+
})
73+
6874
displayConnectors(resp.Connectors)
6975

7076
return nil
@@ -92,8 +98,8 @@ func displayConnectors(connectors []*apiv1.Connector) {
9298
{Align: simpletable.AlignLeft, Text: p.Id},
9399
{Align: simpletable.AlignLeft, Text: p.Plugin},
94100
{Align: simpletable.AlignLeft, Text: p.PipelineId},
95-
{Align: simpletable.AlignLeft, Text: p.CreatedAt.AsTime().String()},
96-
{Align: simpletable.AlignLeft, Text: p.UpdatedAt.AsTime().String()},
101+
{Align: simpletable.AlignLeft, Text: internal.PrintTime(p.CreatedAt)},
102+
{Align: simpletable.AlignLeft, Text: internal.PrintTime(p.UpdatedAt)},
97103
}
98104

99105
table.Body.Cells = append(table.Body.Cells, r)

0 commit comments

Comments
 (0)