Skip to content

Commit

Permalink
Generate commands json and render uipathcli documentation pages
Browse files Browse the repository at this point in the history
- Added a new command to display all available commands
- Implemented simple website to display the available commands
- Extended github actions CI workflow to deploy static assets
  on GitHub Pages
  • Loading branch information
thschmitt committed Oct 2, 2023
1 parent 049b26d commit 5e681df
Show file tree
Hide file tree
Showing 8 changed files with 531 additions and 2 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ jobs:
path: build/packages/
if-no-files-found: error

publish_pages:
needs: build
#if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Download packages
uses: actions/download-artifact@v3
with:
name: packages
path: build/packages/
- name: Generate commands
run: |
tar -xzvf build/packages/uipathcli-linux-amd64.tar.gz
./uipath commands > documentation/commands.json
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: 'documentation'
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v2

release:
needs: build
if: github.ref == 'refs/heads/main'
Expand Down
41 changes: 40 additions & 1 deletion commandline/command_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,23 @@ func (b CommandBuilder) createConfigSetCommand() *cli.Command {
}

func (b CommandBuilder) loadDefinitions(args []string, version string) ([]parser.Definition, error) {
if len(args) > 1 && args[1] == "commands" {
all, err := b.DefinitionProvider.Index(version)
if err != nil {
return nil, err
}
definitions := []parser.Definition{}
for _, d := range all {
definition, err := b.DefinitionProvider.Load(d.Name, version)
if err != nil {
return nil, err
}
if definition != nil {
definitions = append(definitions, *definition)
}
}
return definitions, nil
}
if len(args) <= 1 || strings.HasPrefix(args[1], "--") {
return b.DefinitionProvider.Index(version)
}
Expand All @@ -699,6 +716,27 @@ func (b CommandBuilder) loadAutocompleteDefinitions(args []string, version strin
return b.loadDefinitions(args, version)
}

func (b CommandBuilder) createDisplayCommands(definitions []parser.Definition) *cli.Command {
return &cli.Command{
Name: "commands",
Description: "Display available commands",
Flags: []cli.Flag{
b.HelpFlag(),
},
Hidden: true,
HideHelp: true,
Action: func(context *cli.Context) error {
handler := newDisplayCommandsHandler()
output, err := handler.Display(definitions)
if err != nil {
return err
}
fmt.Fprintln(b.StdOut, output)
return nil
},
}
}

func (b CommandBuilder) createServiceCommands(definitions []parser.Definition) []*cli.Command {
commands := []*cli.Command{}
for _, e := range definitions {
Expand Down Expand Up @@ -740,7 +778,8 @@ func (b CommandBuilder) Create(args []string) ([]*cli.Command, error) {
servicesCommands := b.createServiceCommands(definitions)
autocompleteCommand := b.createAutoCompleteCommand(version)
configCommand := b.createConfigCommand()
commands := append(servicesCommands, autocompleteCommand, configCommand)
displayCommands := b.createDisplayCommands(definitions)
commands := append(servicesCommands, autocompleteCommand, configCommand, displayCommands)
return commands, nil
}

Expand Down
1 change: 0 additions & 1 deletion commandline/definition_file_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ func (s *DefinitionFileStore) Read(name string, version string) (*DefinitionData
return nil, err
}
definition := NewDefinitionData(name, version, data)
s.definitions = append(s.definitions, *definition)
return definition, err
}
}
Expand Down
130 changes: 130 additions & 0 deletions commandline/display_commands_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package commandline

import (
"encoding/json"
"sort"

"github.com/UiPath/uipathcli/parser"
)

// displayCommandsHandler shows all the available commands
type displayCommandsHandler struct {
}

type parameterJson struct {
Name string `json:"name"`
Type string `json:"type"`
Description string `json:"description"`
Required bool `json:"required"`
AllowedValues []interface{} `json:"allowedValues"`
DefaultValue interface{} `json:"defaultValue"`
}

type commandJson struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters []parameterJson `json:"parameters"`
Subcommands []commandJson `json:"subcommands"`
}

func (h displayCommandsHandler) Display(definitions []parser.Definition) (string, error) {
result := commandJson{
Name: "uipath",
Description: "Command line interface to simplify, script and automate API calls for UiPath services",
Subcommands: h.convertDefinitionsToCommands(definitions),
}
bytes, err := json.MarshalIndent(result, "", " ")
if err != nil {
return "", err
}
return string(bytes), nil
}

func (h displayCommandsHandler) convertDefinitionsToCommands(definitions []parser.Definition) []commandJson {
commands := []commandJson{}
for _, d := range definitions {
command := h.convertDefinitionToCommands(d)
commands = append(commands, command)
}
return commands
}

func (h displayCommandsHandler) convertDefinitionToCommands(definition parser.Definition) commandJson {
categories := map[string]commandJson{}

for _, op := range definition.Operations {
if op.Category == nil {
command := h.convertOperationToCommand(op)
categories[command.Name] = command
} else {
h.createOrUpdateCategory(op, categories)
}
}

commands := []commandJson{}
for _, command := range categories {
commands = append(commands, command)
}

h.sort(commands)
for _, command := range commands {
h.sort(command.Subcommands)
}
return commandJson{
Name: definition.Name,
Subcommands: commands,
}
}

func (h displayCommandsHandler) createOrUpdateCategory(operation parser.Operation, categories map[string]commandJson) {
command, found := categories[operation.Category.Name]
if !found {
command = h.createCategoryCommand(operation)
}
command.Subcommands = append(command.Subcommands, h.convertOperationToCommand(operation))
categories[operation.Category.Name] = command
}

func (h displayCommandsHandler) createCategoryCommand(operation parser.Operation) commandJson {
return commandJson{
Name: operation.Category.Name,
Description: operation.Category.Description,
}
}

func (h displayCommandsHandler) convertOperationToCommand(operation parser.Operation) commandJson {
return commandJson{
Name: operation.Name,
Description: operation.Description,
Parameters: h.convertParametersToCommandParameters(operation.Parameters),
}
}

func (h displayCommandsHandler) convertParametersToCommandParameters(parameters []parser.Parameter) []parameterJson {
result := []parameterJson{}
for _, p := range parameters {
result = append(result, h.convertParameterToCommandParameter(p))
}
return result
}

func (h displayCommandsHandler) convertParameterToCommandParameter(parameter parser.Parameter) parameterJson {
return parameterJson{
Name: parameter.Name,
Description: parameter.Description,
Type: parameter.Type,
Required: parameter.Required,
AllowedValues: parameter.AllowedValues,
DefaultValue: parameter.DefaultValue,
}
}

func (h displayCommandsHandler) sort(commands []commandJson) {
sort.Slice(commands, func(i, j int) bool {
return commands[i].Name < commands[j].Name
})
}

func newDisplayCommandsHandler() *displayCommandsHandler {
return &displayCommandsHandler{}
}
145 changes: 145 additions & 0 deletions documentation/css/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
* {
color: #273139;
font-family: noto-sans, "Noto Sans JP", "Noto Sans KR", "Noto Sans SC", "Noto Sans TC", "Noto Sans", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial,sans-serif;
}

body {
margin: 0;
padding: 0;
}

h1 {
font-size: 26px;
}

h2 {
font-size: 20px;
border-bottom: 1px solid rgb(164, 177, 184);
width: 90%;
}

a {
color: #0067df;
text-decoration: none;
font-weight: 600;
}

.main {
margin: 20px;
}

.header {
height: 48px;
border-bottom: 1px solid rgb(207, 216, 221);
display: flex;
flex-direction: row;
}

.header-icon {
padding-top: 6px;
padding-left: 8px;
line-height: 48px;
vertical-align: middle;
}

.header-text {
padding-left: 15px;
font-size: 16px;
font-weight: 600;
line-height: 48px;
vertical-align: middle;
}

.breadcrumb
{
color: #526069;
font-size: 14px;
font-weight: 600;
display: inline-block;
}

.breadcrumb ol {
padding-left: 0;
list-style: none;
}

.breadcrumb li {
float: left;
}

.breadcrumb li:after
{
content: '/';
padding-left: 8px;
padding-right: 8px;
display: inline;
}

.breadcrumb li:last-child
{
font-weight: 400;
}

.breadcrumb li:last-child:after
{
content: '';
}

.footer {
margin-top: 20px;
height: 100px;
background: rgb(29, 29, 30);
display: flex;
flex-direction: row;
justify-content: space-between;
}

.footer-icon {
padding-top: 8px;
padding-left: 8px;
line-height: 100px;
vertical-align: middle;
}

.footer-text {
padding-right: 8px;
color: rgb(89, 90, 92);
line-height: 100px;
vertical-align: middle;
}

.usage {
padding: 5px;
background-color: #f7f7f7;
border-radius: 4px;
border: 1px solid #e1e1e8;
font-family: monospace;
}

.parameters {
padding-left: 0;
list-style: none;
}

.parameter {
margin: 20px;
}

.parameter-name {
padding: 2px;
background-color: #f7f7f7;
border-radius: 4px;
border: 1px solid #e1e1e8;
font-family: monospace;
}

.parameter-description {
margin-top: 10px;
margin-bottom: 10px;
display: inline-block;
width: 100%;
}

.parameter-allowed-values ul {
list-style-type: square;
}
Binary file added documentation/favicon.ico
Binary file not shown.
Loading

0 comments on commit 5e681df

Please sign in to comment.