Skip to content

Commit

Permalink
Merge pull request #108 from trickest/feat/private-tools
Browse files Browse the repository at this point in the history
Add private tools support
  • Loading branch information
mhmdiaa authored Jan 15, 2024
2 parents e8729ee + 51b19db commit d024161
Show file tree
Hide file tree
Showing 15 changed files with 505 additions and 40 deletions.
64 changes: 53 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ trickest list --project <project_name> --space <space_name>
|-----------|---------|---------|----------------------------------------------------|
| --project | string | / | The name of the project to be listed. |
| --space | string | / | The name of the space to which the project belongs |
| --json | boolean | / | Display output in JSON format |
| --json | boolean | false | Display output in JSON format |
| --url | string | / | URL for referencing a space |


Expand All @@ -109,8 +109,8 @@ trickest get --workflow <workflow_name> --space <space_name> [--watch]
| --project | string | / | The name of the project to which the workflow belongs |
| --workflow | string | / | The name of the workflow |
| --run | string | / | Get the status of a specific run |
| --watch | boolean | / | Option to track execution status in case workflow is in running state |
| --json | boolean | / | Display output in JSON format |
| --watch | boolean | false | Option to track execution status in case workflow is in running state |
| --json | boolean | false | Display output in JSON format |
| --url | string | / | URL for referencing a space |

##### If the supplied workflow has a running execution, you can jump in and watch it running with the `--watch` flag!
Expand All @@ -126,12 +126,12 @@ trickest execute --workflow <workflow_or_tool_name> --space <space_name> --confi
|------------------|---------|---------|---------------------------------------------------------------------------------------------------------------------------------------------|
| --config | file | / | YAML file for run configuration |
| --workflow | string | / | Workflow from the Library to be executed |
| --max | boolean | / | Use maximum number of machines for workflow execution |
| --max | boolean | false | Use maximum number of machines for workflow execution |
| --output | string | / | A comma-separated list of nodes whose outputs should be downloaded when the execution is finished |
| --output-all | boolean | / | Download all outputs when the execution is finished |
| --output-all | boolean | false | Download all outputs when the execution is finished |
| --output-dir | string | . | Path to the directory which should be used to store outputs |
| --show-params | boolean | / | Show parameters in the workflow tree |
| --watch | boolean | / | Option to track execution status in case workflow is in running state |
| --show-params | boolean | false | Show parameters in the workflow tree |
| --watch | boolean | false | Option to track execution status in case workflow is in running state |
| --set-name | string | / | Sets the new workflow name and will copy the workflow to space and project supplied |
| --ci | boolean | false | Enable CI mode (in-progress executions will be stopped when the CLI is forcefully stopped - if not set, you will be asked for confirmation) |
| --create-project | boolean | false | If the project doesn't exist, create one using the project flag as its name (or workflow/tool name if project flag is not set) |
Expand Down Expand Up @@ -227,9 +227,6 @@ Use **library search** to search all Trickest tools & workflows available in the
trickest library search subdomain takeover
```

[<img src="./banner.png" />](https://trickest.io/auth/register)


## Files command
Interact with the Trickest file storage

Expand All @@ -244,7 +241,7 @@ trickest files get --file my_file.txt --output-dir out
|----------------------|--------|----------|---------------------------------------------------------------------|
| --file | string | / | File or files (comma-separated) |
| --output-dir | string | / | Path to directory which should be used to store files (default ".") |
| --partial-name-match | boolean | / | Get all files with a partial name match |
| --partial-name-match | boolean | false | Get all files with a partial name match |

#### Create files
Use the **create** command with the **--file** flag to upload one or more files
Expand All @@ -270,7 +267,52 @@ trickest files delete --file delete_me.txt
| --file | string | / | File or files (comma-separated) |


## Tools command
Manage [private tools](https://trickest.com/docs/tutorials/private-tools/private-tools-library/)

⚒️ Learn how to add your first tool integration [here](https://trickest.com/docs/tutorials/private-tools/dockerfile-and-trickest-yaml/).

#### Create a new private tool integration
```
trickest tools create --file tool.yaml
```

| Flag | Type | Default | Description |
|----------------------|--------|----------|---------------------------------------------------------------------|
| --file | string | / | YAML file for tool definition |

#### Update a private tool integration
```
trickest tools update --file tool.yaml
```

| Flag | Type | Default | Description |
|----------------------|--------|----------|---------------------------------------------------------------------|
| --file | string | / | YAML file for tool definition |

#### List private tool integrations
```
trickest tools list
```

| Flag | Type | Default | Description |
|----------------------|---------|-------------|---------------------------------------------------------------------|
| --json | boolean | false | Display output in JSON format |

#### Delete a private tool integration
```
trickest tools delete --name "my-tool"
```

| Flag | Type | Default | Description |
|----------------------|--------|----------|---------------------------------------------------------------------|
| --id | string | / | ID of the tool to delete |
| --name | string | / | Name of the tool to delete |

## Report Bugs / Feedback
We look forward to any feedback you want to share with us or if you're stuck with a problem you can contact us at [[email protected]](mailto:[email protected]).

You can also create an [Issue](https://github.com/trickest/trickest-cli/issues/new/choose) in the Github repository.

[<img src="./banner.png" />](https://trickest.io/auth/register)

29 changes: 29 additions & 0 deletions cmd/library/library.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package library

import (
"encoding/json"
"fmt"
"strings"

"github.com/spf13/cobra"
"github.com/trickest/trickest-cli/types"
"github.com/xlab/treeprint"
)

var (
Expand All @@ -27,3 +33,26 @@ func init() {
command.Root().HelpFunc()(command, strings)
})
}

func PrintTools(tools []types.Tool, jsonOutput bool) {
var output string
if jsonOutput {
data, err := json.Marshal(tools)
if err != nil {
fmt.Println("Error marshalling project data")
return
}
output = string(data)
} else {
tree := treeprint.New()
tree.SetValue("Tools")
for _, tool := range tools {
branch := tree.AddBranch(tool.Name + " [" + strings.TrimPrefix(tool.SourceURL, "https://") + "]")
branch.AddNode("\U0001f4cb \033[3m" + tool.Description + "\033[0m") //📋
}

output = tree.String()
}

fmt.Println(output)
}
29 changes: 1 addition & 28 deletions cmd/library/libraryListTools.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package library

import (
"encoding/json"
"fmt"
"math"
"strings"

"github.com/spf13/cobra"
"github.com/trickest/trickest-cli/cmd/list"
"github.com/trickest/trickest-cli/types"
"github.com/xlab/treeprint"
)

// libraryListToolsCmd represents the libraryListTools command
Expand All @@ -20,7 +16,7 @@ var libraryListToolsCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
tools := list.GetTools(math.MaxInt, "", "")
if len(tools) > 0 {
printTools(tools, jsonOutput)
PrintTools(tools, jsonOutput)
} else {
fmt.Println("Couldn't find any tool in the library!")
}
Expand All @@ -31,26 +27,3 @@ func init() {
libraryListCmd.AddCommand(libraryListToolsCmd)
libraryListToolsCmd.Flags().BoolVar(&jsonOutput, "json", false, "Display output in JSON format")
}

func printTools(tools []types.Tool, jsonOutput bool) {
var output string
if jsonOutput {
data, err := json.Marshal(tools)
if err != nil {
fmt.Println("Error marshalling project data")
return
}
output = string(data)
} else {
tree := treeprint.New()
tree.SetValue("Tools")
for _, tool := range tools {
branch := tree.AddBranch(tool.Name + " [" + strings.TrimPrefix(tool.SourceURL, "https://") + "]")
branch.AddNode("\U0001f4cb \033[3m" + tool.Description + "\033[0m") //📋
}

output = tree.String()
}

fmt.Println(output)
}
2 changes: 1 addition & 1 deletion cmd/library/librarySearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var librarySearchCmd = &cobra.Command{
fmt.Println(output)
} else {
if len(tools) > 0 {
printTools(tools, jsonOutput)
PrintTools(tools, jsonOutput)
} else {
fmt.Println("Couldn't find any tool in the library that matches the search!")
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/trickest/trickest-cli/cmd/library"
"github.com/trickest/trickest-cli/cmd/list"
"github.com/trickest/trickest-cli/cmd/output"
"github.com/trickest/trickest-cli/cmd/tools"
"github.com/trickest/trickest-cli/util"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -52,6 +53,7 @@ func init() {
RootCmd.AddCommand(execute.ExecuteCmd)
RootCmd.AddCommand(get.GetCmd)
RootCmd.AddCommand(files.FilesCmd)
RootCmd.AddCommand(tools.ToolsCmd)
// RootCmd.AddCommand(export.ExportCmd)
}

Expand Down
154 changes: 154 additions & 0 deletions cmd/tools/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package tools

import (
"encoding/json"
"fmt"
"net/http"
"os"
"strings"

"github.com/go-yaml/yaml"
"github.com/google/uuid"
"github.com/spf13/cobra"
"github.com/trickest/trickest-cli/client/request"
"github.com/trickest/trickest-cli/types"
"github.com/trickest/trickest-cli/util"
)

var toolOutputTypes = map[string]string{
"file": "2",
"folder": "3",
}

var ToolsCmd = &cobra.Command{
Use: "tools",
Short: "Manage private tools",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}

func init() {
ToolsCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
_ = ToolsCmd.Flags().MarkHidden("workflow")
_ = ToolsCmd.Flags().MarkHidden("project")
_ = ToolsCmd.Flags().MarkHidden("space")
_ = ToolsCmd.Flags().MarkHidden("url")

command.Root().HelpFunc()(command, strings)
})
}

func ListPrivateTools(name string) ([]types.Tool, error) {
endpoint := "library/tool/?public=False"
endpoint += fmt.Sprintf("&vault=%s", util.GetVault())
if name != "" {
endpoint += "&search=" + name
} else {
endpoint += "&page_size=100"
}

resp := request.Trickest.Get().Do(endpoint)
if resp == nil || resp.Status() != http.StatusOK {
request.ProcessUnexpectedResponse(resp)
}

var tools types.Tools
err := json.Unmarshal(resp.Body(), &tools)
if err != nil {
return nil, fmt.Errorf("couldn't parse API response: %s", err)
}

return tools.Results, nil
}

func getToolIDByName(name string) (uuid.UUID, error) {
tools, err := ListPrivateTools(name)
if err != nil {
return uuid.Nil, fmt.Errorf("couldn't search for %s: %w", name, err)
}

if len(tools) == 0 {
return uuid.Nil, fmt.Errorf("couldn't find tool '%s'", name)
}

if len(tools) > 1 {
return uuid.Nil, fmt.Errorf("found more than one match for '%s'", name)
}

return tools[0].ID, nil
}

func createToolImportRequestFromYAML(fileName string) (types.ToolImportRequest, error) {
data, err := os.ReadFile(fileName)
if err != nil {
err = fmt.Errorf("couldn't read %s: %w", fileName, err)
return types.ToolImportRequest{}, err
}

var toolImportRequest types.ToolImportRequest
err = yaml.Unmarshal(data, &toolImportRequest)
if err != nil {
err = fmt.Errorf("couldn't parse %s: %w", fileName, err)
return types.ToolImportRequest{}, err
}

categoryID, err := util.GetCategoryIDByName(toolImportRequest.Category)
if err != nil {
err = fmt.Errorf("couldn't use the category '%s': %w", toolImportRequest.Category, err)
return types.ToolImportRequest{}, err
}

toolImportRequest.CategoryID = categoryID
toolImportRequest.VaultInfo = util.GetVault()
toolImportRequest.OutputType = toolOutputTypes[toolImportRequest.OutputType]
for name := range toolImportRequest.Inputs {
if input, ok := toolImportRequest.Inputs[name]; ok {
input.Type = strings.ToUpper(toolImportRequest.Inputs[name].Type)
toolImportRequest.Inputs[name] = input
}
}

return toolImportRequest, nil
}

func importTool(fileName string, isUpdate bool) (string, uuid.UUID, error) {
toolImportRequest, err := createToolImportRequestFromYAML(fileName)
if err != nil {
return "", uuid.Nil, err
}

toolJSON, err := json.Marshal(toolImportRequest)
if err != nil {
return "", uuid.Nil, fmt.Errorf("couldn't encode %s: %w", fileName, err)
}

var resp *request.Response
if isUpdate {
toolName := toolImportRequest.Name
toolID, err := getToolIDByName(toolName)
if err != nil {
return "", uuid.Nil, fmt.Errorf("couldn't import '%s': %w", toolName, err)
}
resp = request.Trickest.Patch().Body(toolJSON).DoF("library/tool/%s/", toolID.String())
} else {
resp = request.Trickest.Post().Body(toolJSON).Do("library/tool/")
}

if resp == nil {
return "", uuid.Nil, fmt.Errorf("couldn't import %s", fileName)
}

if resp.Status() != http.StatusCreated && resp.Status() != http.StatusOK {
request.ProcessUnexpectedResponse(resp)
}

var importedTool types.Tool
err = json.Unmarshal(resp.Body(), &importedTool)
if err != nil {
return "", uuid.Nil, fmt.Errorf("couldn't import %s: %w", fileName, err)
}

return importedTool.Name, importedTool.ID, nil
}
Loading

0 comments on commit d024161

Please sign in to comment.