Skip to content

Commit

Permalink
Add support to analyze and package studio projects
Browse files Browse the repository at this point in the history
  • Loading branch information
thschmitt committed Dec 27, 2024
1 parent 1315e06 commit 266f036
Show file tree
Hide file tree
Showing 46 changed files with 2,277 additions and 166 deletions.
36 changes: 33 additions & 3 deletions auth/browser_launcher.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
package auth

// BrowserLauncher interface for opening browser windows.
type BrowserLauncher interface {
Open(url string) error
import (
"fmt"
"time"

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

// BrowserLauncher tries to open the default browser on the local system.
type BrowserLauncher struct {
Exec utils.ExecProcess
}

func (l BrowserLauncher) Open(url string) error {
cmd := l.openBrowser(url)
err := cmd.Start()
if err != nil {
return err
}
done := make(chan error)
go func() {
done <- cmd.Wait()
}()

select {
case err := <-done:
return err
case <-time.After(5 * time.Second):
return fmt.Errorf("Timed out waiting for browser to start")
}
}

func NewBrowserLauncher() *BrowserLauncher {
return &BrowserLauncher{utils.NewExecProcess()}
}
11 changes: 11 additions & 0 deletions auth/browser_launcher_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build darwin

package auth

import (
"github.com/UiPath/uipathcli/utils"
)

func (l BrowserLauncher) openBrowser(url string) utils.ExecCmd {
return l.Exec.Command("open", url)
}
11 changes: 11 additions & 0 deletions auth/browser_launcher_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build linux

package auth

import (
"github.com/UiPath/uipathcli/utils"
)

func (l BrowserLauncher) openBrowser(url string) utils.ExecCmd {
return l.Exec.Command("xdg-open", url)
}
11 changes: 11 additions & 0 deletions auth/browser_launcher_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//go:build windows

package auth

import (
"github.com/UiPath/uipathcli/utils"
)

func (l BrowserLauncher) openBrowser(url string) utils.ExecCmd {
return l.Exec.Command("rundll32", "url.dll,FileProtocolHandler", url)
}
47 changes: 0 additions & 47 deletions auth/exec_browser_launcher.go

This file was deleted.

30 changes: 15 additions & 15 deletions auth/oauth_authenticator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"runtime"
"strings"
"testing"

"github.com/UiPath/uipathcli/cache"
"github.com/UiPath/uipathcli/utils"
)

func TestOAuthAuthenticatorNotEnabled(t *testing.T) {
Expand All @@ -25,7 +27,7 @@ func TestOAuthAuthenticatorNotEnabled(t *testing.T) {
request := NewAuthenticatorRequest("http:/localhost", map[string]string{})
context := NewAuthenticatorContext("login", config, createIdentityUrl(""), false, false, *request)

authenticator := NewOAuthAuthenticator(cache.NewFileCache(), nil)
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), *NewBrowserLauncher())
result := authenticator.Auth(*context)
if result.Error != "" {
t.Errorf("Expected no error when oauth flow is skipped, but got: %v", result.Error)
Expand All @@ -46,7 +48,7 @@ func TestOAuthAuthenticatorPreservesExistingHeaders(t *testing.T) {
request := NewAuthenticatorRequest("http:/localhost", headers)
context := NewAuthenticatorContext("login", config, createIdentityUrl(""), false, false, *request)

authenticator := NewOAuthAuthenticator(cache.NewFileCache(), nil)
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), *NewBrowserLauncher())
result := authenticator.Auth(*context)
if result.Error != "" {
t.Errorf("Expected no error when oauth flow is skipped, but got: %v", result.Error)
Expand All @@ -65,7 +67,7 @@ func TestOAuthAuthenticatorInvalidConfig(t *testing.T) {
request := NewAuthenticatorRequest("http:/localhost", map[string]string{})
context := NewAuthenticatorContext("login", config, createIdentityUrl(""), false, false, *request)

authenticator := NewOAuthAuthenticator(cache.NewFileCache(), nil)
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), *NewBrowserLauncher())
result := authenticator.Auth(*context)
if result.Error != "Invalid oauth authenticator configuration: Invalid value for clientId: '1'" {
t.Errorf("Expected error with invalid config, but got: %v", result.Error)
Expand Down Expand Up @@ -122,7 +124,7 @@ func TestOAuthFlowIsCached(t *testing.T) {
performLogin(loginUrl, t)
<-resultChannel

authenticator := NewOAuthAuthenticator(cache.NewFileCache(), nil)
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), *NewBrowserLauncher())
result := authenticator.Auth(context)

if result.Error != "" {
Expand Down Expand Up @@ -208,8 +210,15 @@ func TestMissingCodeShowsErrorMessage(t *testing.T) {

func callAuthenticator(context AuthenticatorContext) (url.URL, chan AuthenticatorResult) {
loginChan := make(chan string)
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), NoOpBrowserLauncher{
loginUrlChannel: loginChan,
authenticator := NewOAuthAuthenticator(cache.NewFileCache(), BrowserLauncher{
Exec: utils.NewExecCustomProcess(0, "", "", func(name string, args []string) {
switch runtime.GOOS {
case "windows":
loginChan <- args[1]
default:
loginChan <- args[0]
}
}),
})

resultChannel := make(chan AuthenticatorResult)
Expand Down Expand Up @@ -323,12 +332,3 @@ func (i identityServerFake) writeValidationErrorResponse(response http.ResponseW
response.WriteHeader(400)
_, _ = response.Write([]byte(message))
}

type NoOpBrowserLauncher struct {
loginUrlChannel chan string
}

func (l NoOpBrowserLauncher) Open(url string) error {
l.loginUrlChannel <- url
return nil
}
11 changes: 4 additions & 7 deletions cache/file_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"strconv"
"strings"
"time"

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

const cacheFilePermissions = 0600
const cacheDirectoryPermissions = 0700
const cacheDirectory = "uipath"
const separator = "|"

// The FileCache stores data on disk in order to preserve them across
Expand Down Expand Up @@ -63,15 +63,12 @@ func (c FileCache) readValue(key string) (int64, string, error) {
}

func (c FileCache) cacheFilePath(key string) (string, error) {
userCacheDirectory, err := os.UserCacheDir()
cacheDirectory, err := utils.Directories{}.Cache()
if err != nil {
return "", err
}
cacheDirectory := filepath.Join(userCacheDirectory, cacheDirectory)
_ = os.MkdirAll(cacheDirectory, cacheDirectoryPermissions)

hash := sha256.Sum256([]byte(key))
fileName := fmt.Sprintf("%x.cache", hash)
fileName := fmt.Sprintf("%x", hash)
return filepath.Join(cacheDirectory, fileName), nil
}

Expand Down
14 changes: 14 additions & 0 deletions definitions/studio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
openapi: 3.0.1
info:
title: UiPath Studio
description: UiPath Studio
version: v1
servers:
- url: https://cloud.uipath.com/{organization}/studio_/backend
description: The production url
variables:
organization:
description: The organization name (or id)
default: my-org
paths:
{}
4 changes: 4 additions & 0 deletions log/debug_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func (l DebugLogger) LogResponse(response ResponseInfo) {
fmt.Fprint(l.writer, "\n\n\n")
}

func (l DebugLogger) Log(message string) {
fmt.Fprint(l.writer, message)
}

func (l DebugLogger) LogError(message string) {
fmt.Fprint(l.writer, message)
}
Expand Down
3 changes: 3 additions & 0 deletions log/default_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func (l *DefaultLogger) LogRequest(request RequestInfo) {
func (l DefaultLogger) LogResponse(response ResponseInfo) {
}

func (l DefaultLogger) Log(message string) {
}

func (l DefaultLogger) LogError(message string) {
fmt.Fprint(l.writer, message)
}
Expand Down
1 change: 1 addition & 0 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package log
// The Logger interface which is used to provide additional information to the
// user about what operations the CLI is performing.
type Logger interface {
Log(message string)
LogError(message string)
LogRequest(request RequestInfo)
LogResponse(response ResponseInfo)
Expand Down
11 changes: 7 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/UiPath/uipathcli/plugin"
plugin_digitizer "github.com/UiPath/uipathcli/plugin/digitizer"
plugin_orchestrator "github.com/UiPath/uipathcli/plugin/orchestrator"
plugin_studio "github.com/UiPath/uipathcli/plugin/studio"
"github.com/UiPath/uipathcli/utils"
)

Expand All @@ -27,7 +28,7 @@ var embedded embed.FS
func authenticators() []auth.Authenticator {
return []auth.Authenticator{
auth.NewPatAuthenticator(),
auth.NewOAuthAuthenticator(cache.NewFileCache(), auth.NewExecBrowserLauncher()),
auth.NewOAuthAuthenticator(cache.NewFileCache(), *auth.NewBrowserLauncher()),
auth.NewBearerAuthenticator(cache.NewFileCache()),
}
}
Expand Down Expand Up @@ -63,9 +64,11 @@ func main() {
commandline.NewDefinitionFileStore(os.Getenv("UIPATH_DEFINITIONS_PATH"), embedded),
parser.NewOpenApiParser(),
[]plugin.CommandPlugin{
plugin_digitizer.DigitizeCommand{},
plugin_orchestrator.UploadCommand{},
plugin_orchestrator.DownloadCommand{},
plugin_digitizer.NewDigitizeCommand(),
plugin_orchestrator.NewUploadCommand(),
plugin_orchestrator.NewDownloadCommand(),
plugin_studio.NewPackagePackCommand(),
plugin_studio.NewPackageAnalyzeCommand(),
},
),
*configProvider,
Expand Down
29 changes: 17 additions & 12 deletions plugin/digitizer/digitize_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,9 @@ func (c DigitizeCommand) createDigitizeRequest(context plugin.ExecutionContext,
var err error
file := context.Input
if file == nil {
file, err = c.getFileParameter(context.Parameters)
if err != nil {
return nil, err
}
file = c.getFileParameter(context.Parameters)
}
contentType, _ := c.getParameter("content-type", context.Parameters)
contentType := c.getParameter("content-type", context.Parameters)
if contentType == "" {
contentType = "application/octet-stream"
}
Expand Down Expand Up @@ -262,33 +259,37 @@ func (c DigitizeCommand) sendRequest(request *http.Request, insecure bool) (*htt
}

func (c DigitizeCommand) getProjectId(parameters []plugin.ExecutionParameter) string {
projectId, _ := c.getParameter("project-id", parameters)
projectId := c.getParameter("project-id", parameters)
if projectId == "" {
projectId = "00000000-0000-0000-0000-000000000000"
}
return projectId
}

func (c DigitizeCommand) getParameter(name string, parameters []plugin.ExecutionParameter) (string, error) {
func (c DigitizeCommand) getParameter(name string, parameters []plugin.ExecutionParameter) string {
result := ""
for _, p := range parameters {
if p.Name == name {
if data, ok := p.Value.(string); ok {
return data, nil
result = data
break
}
}
}
return "", fmt.Errorf("Could not find '%s' parameter", name)
return result
}

func (c DigitizeCommand) getFileParameter(parameters []plugin.ExecutionParameter) (utils.Stream, error) {
func (c DigitizeCommand) getFileParameter(parameters []plugin.ExecutionParameter) utils.Stream {
var result utils.Stream
for _, p := range parameters {
if p.Name == "file" {
if stream, ok := p.Value.(utils.Stream); ok {
return stream, nil
result = stream
break
}
}
}
return nil, fmt.Errorf("Could not find 'file' parameter")
return result
}

func (c DigitizeCommand) logRequest(logger log.Logger, request *http.Request) {
Expand All @@ -304,3 +305,7 @@ func (c DigitizeCommand) logResponse(logger log.Logger, response *http.Response,
responseInfo := log.NewResponseInfo(response.StatusCode, response.Status, response.Proto, response.Header, bytes.NewReader(body))
logger.LogResponse(*responseInfo)
}

func NewDigitizeCommand() *DigitizeCommand {
return &DigitizeCommand{}
}
Loading

0 comments on commit 266f036

Please sign in to comment.