Skip to content

Commit

Permalink
feat(CLI): add connectors describe (#2082)
Browse files Browse the repository at this point in the history
* 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 <lovro.mazgon@gmail.com>

* Update cmd/conduit/root/connectors/describe.go

Co-authored-by: Lovro Mažgon <lovro.mazgon@gmail.com>

* Update cmd/conduit/internal/print_utils.go

Co-authored-by: Lovro Mažgon <lovro.mazgon@gmail.com>

* display processors on pipelines desc and sort

* add some tests

* lint

---------

Co-authored-by: Lovro Mažgon <lovro.mazgon@gmail.com>
  • Loading branch information
raulb and lovromazgon authored Jan 21, 2025
1 parent 37457e2 commit ac8fff0
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 95 deletions.
72 changes: 72 additions & 0 deletions cmd/conduit/internal/print_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright © 2025 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal

import (
"fmt"
"strings"

apiv1 "github.com/conduitio/conduit/proto/api/v1"
"google.golang.org/protobuf/types/known/timestamppb"
)

// Indentation returns a string with the number of spaces equal to the level
func Indentation(level int) string {
return strings.Repeat(" ", level)
}

// PrintStatusFromProtoString returns a human-readable status from a proto status
func PrintStatusFromProtoString(protoStatus string) string {
return PrettyProtoEnum("STATUS_", protoStatus)
}

// PrettyProtoEnum returns a human-readable string from a proto enum
func PrettyProtoEnum(prefix, protoEnum string) string {
return strings.ToLower(
strings.TrimPrefix(protoEnum, prefix),
)
}

// PrintTime returns a human-readable time from a timestamp
func PrintTime(ts *timestamppb.Timestamp) string {
return ts.AsTime().Format("2006-01-02T15:04:05Z")
}

// DisplayProcessors prints the processors in a human-readable format
func DisplayProcessors(processors []*apiv1.Processor, indent int) {
if len(processors) == 0 {
return
}

fmt.Printf("%sProcessors:\n", Indentation(indent))

for _, p := range processors {
fmt.Printf("%s- ID: %s\n", Indentation(indent+1), p.Id)
fmt.Printf("%sPlugin: %s\n", Indentation(indent+2), p.Plugin)

if p.Condition != "" {
fmt.Printf("%sCondition: %s\n", Indentation(indent+2), p.Condition)
}

fmt.Printf("%sConfig:\n", Indentation(indent+2))
for name, value := range p.Config.Settings {
fmt.Printf("%s%s: %s\n", Indentation(indent+3), name, value)
}
fmt.Printf("%sWorkers: %d\n", Indentation(indent+3), p.Config.Workers)

fmt.Printf("%sCreated At: %s\n", Indentation(indent+2), PrintTime(p.CreatedAt))
fmt.Printf("%sUpdated At: %s\n", Indentation(indent+2), PrintTime(p.UpdatedAt))
}
}
5 changes: 5 additions & 0 deletions cmd/conduit/root/connectorplugins/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package connectorplugins
import (
"context"
"fmt"
"sort"

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

sort.Slice(resp.Plugins, func(i, j int) bool {
return resp.Plugins[i].Name < resp.Plugins[j].Name
})

displayConnectorPlugins(resp.Plugins)

return nil
Expand Down
1 change: 1 addition & 0 deletions cmd/conduit/root/connectors/connectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func (c *ConnectorsCommand) Aliases() []string { return []string{"connector"} }
func (c *ConnectorsCommand) SubCommands() []ecdysis.Command {
return []ecdysis.Command{
&ListCommand{},
&DescribeCommand{},
}
}

Expand Down
133 changes: 133 additions & 0 deletions cmd/conduit/root/connectors/describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright © 2025 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package connectors

import (
"context"
"fmt"

"github.com/conduitio/conduit/cmd/conduit/api"
"github.com/conduitio/conduit/cmd/conduit/cecdysis"
"github.com/conduitio/conduit/cmd/conduit/internal"
"github.com/conduitio/conduit/pkg/foundation/cerrors"
apiv1 "github.com/conduitio/conduit/proto/api/v1"
"github.com/conduitio/ecdysis"
)

var (
_ cecdysis.CommandWithExecuteWithClient = (*DescribeCommand)(nil)
_ ecdysis.CommandWithAliases = (*DescribeCommand)(nil)
_ ecdysis.CommandWithDocs = (*DescribeCommand)(nil)
_ ecdysis.CommandWithArgs = (*DescribeCommand)(nil)
)

type DescribeArgs struct {
ConnectorID string
}

type DescribeCommand struct {
args DescribeArgs
}

func (c *DescribeCommand) Usage() string { return "describe" }

func (c *DescribeCommand) Docs() ecdysis.Docs {
return ecdysis.Docs{
Short: "Describe an existing connector",
Long: `This command requires Conduit to be already running since it will describe a connector registered
by Conduit. You can list existing connectors with the 'conduit connectors list' command.`,
Example: "conduit connectors describe connector:source\n" +
"conduit connectors desc connector:destination",
}
}

func (c *DescribeCommand) Aliases() []string { return []string{"desc"} }

func (c *DescribeCommand) Args(args []string) error {
if len(args) == 0 {
return cerrors.Errorf("requires a connector ID")
}

if len(args) > 1 {
return cerrors.Errorf("too many arguments")
}

c.args.ConnectorID = args[0]
return nil
}

func (c *DescribeCommand) ExecuteWithClient(ctx context.Context, client *api.Client) error {
resp, err := client.ConnectorServiceClient.GetConnector(ctx, &apiv1.GetConnectorRequest{
Id: c.args.ConnectorID,
})
if err != nil {
return fmt.Errorf("failed to get connector: %w", err)
}

var processors []*apiv1.Processor

for _, processorID := range resp.Connector.ProcessorIds {
processor, err := client.ProcessorServiceClient.GetProcessor(ctx, &apiv1.GetProcessorRequest{
Id: processorID,
})
if err != nil {
return fmt.Errorf("failed to get processor: %w", err)
}
processors = append(processors, processor.Processor)
}

displayConnector(resp.Connector, processors)

return nil
}

func displayConnector(connector *apiv1.Connector, processors []*apiv1.Processor) {
if connector == nil {
return
}

fmt.Printf("ID: %s\n", connector.Id)

var connectorType string

switch connector.Type {
case apiv1.Connector_TYPE_SOURCE:
connectorType = "Source"
case apiv1.Connector_TYPE_DESTINATION:
connectorType = "Destination"
case apiv1.Connector_TYPE_UNSPECIFIED:
connectorType = "Unspecified"
default:
connectorType = "Unknown"
}

fmt.Printf("Type: %s\n", connectorType)
fmt.Printf("Plugin: %s\n", connector.Plugin)
fmt.Printf("Pipeline ID: %s\n", connector.PipelineId)

displayConnectorConfig(connector.Config)
fmt.Printf("Created At: %s\n", internal.PrintTime(connector.CreatedAt))
fmt.Printf("Updated At: %s\n", internal.PrintTime(connector.UpdatedAt))

internal.DisplayProcessors(processors, 0)
}

func displayConnectorConfig(cfg *apiv1.Connector_Config) {
fmt.Println("Config:")

for name, value := range cfg.Settings {
fmt.Printf("%s%s: %s\n", internal.Indentation(1), name, value)
}
}
56 changes: 56 additions & 0 deletions cmd/conduit/root/connectors/describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright © 2025 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package connectors

import (
"testing"

"github.com/matryer/is"
)

func TestDescribeExecutionNoArgs(t *testing.T) {
is := is.New(t)

c := DescribeCommand{}
err := c.Args([]string{})

expected := "requires a connector ID"

is.True(err != nil)
is.Equal(err.Error(), expected)
}

func TestDescribeExecutionMultipleArgs(t *testing.T) {
is := is.New(t)

c := DescribeCommand{}
err := c.Args([]string{"foo", "bar"})

expected := "too many arguments"

is.True(err != nil)
is.Equal(err.Error(), expected)
}

func TestDescribeExecutionCorrectArgs(t *testing.T) {
is := is.New(t)
connectorID := "my-connector"

c := DescribeCommand{}
err := c.Args([]string{connectorID})

is.NoErr(err)
is.Equal(c.args.ConnectorID, connectorID)
}
10 changes: 8 additions & 2 deletions cmd/conduit/root/connectors/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ package connectors
import (
"context"
"fmt"
"sort"

"github.com/alexeyco/simpletable"
"github.com/conduitio/conduit/cmd/conduit/api"
"github.com/conduitio/conduit/cmd/conduit/cecdysis"
"github.com/conduitio/conduit/cmd/conduit/internal"
apiv1 "github.com/conduitio/conduit/proto/api/v1"
"github.com/conduitio/ecdysis"
)
Expand Down Expand Up @@ -65,6 +67,10 @@ func (c *ListCommand) ExecuteWithClient(ctx context.Context, client *api.Client)
return fmt.Errorf("failed to list connectors: %w", err)
}

sort.Slice(resp.Connectors, func(i, j int) bool {
return resp.Connectors[i].Id < resp.Connectors[j].Id
})

displayConnectors(resp.Connectors)

return nil
Expand Down Expand Up @@ -92,8 +98,8 @@ func displayConnectors(connectors []*apiv1.Connector) {
{Align: simpletable.AlignLeft, Text: p.Id},
{Align: simpletable.AlignLeft, Text: p.Plugin},
{Align: simpletable.AlignLeft, Text: p.PipelineId},
{Align: simpletable.AlignLeft, Text: p.CreatedAt.AsTime().String()},
{Align: simpletable.AlignLeft, Text: p.UpdatedAt.AsTime().String()},
{Align: simpletable.AlignLeft, Text: internal.PrintTime(p.CreatedAt)},
{Align: simpletable.AlignLeft, Text: internal.PrintTime(p.UpdatedAt)},
}

table.Body.Cells = append(table.Body.Cells, r)
Expand Down
Loading

0 comments on commit ac8fff0

Please sign in to comment.