diff --git a/.gitignore b/.gitignore index 93e55e2ee..a5199b61f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ test/c8y_test/debug.test test/c8y_test/application.setup.properties .cumulocity/ temp-example/ +*.tmp c8y.env c8y.activitylog.*.json c8y~ diff --git a/go.mod b/go.mod index c888c1958..7ee72aa19 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.3.0 - github.com/reubenmiller/go-c8y v0.10.0-rc.6 + github.com/reubenmiller/go-c8y v0.11.0-rc.2 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/sergi/go-diff v1.2.0 // indirect github.com/sethvargo/go-password v0.2.0 diff --git a/go.sum b/go.sum index 4ca3b2892..6f399c560 100644 --- a/go.sum +++ b/go.sum @@ -437,8 +437,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/reubenmiller/go-c8y v0.10.0-rc.6 h1:bP5l2+gJZbTqY5FVTUOR/Z8urtZ47BuFVh+EeX8xqMo= -github.com/reubenmiller/go-c8y v0.10.0-rc.6/go.mod h1:aSU0AZdT/IWCn87wMveejGvwQE3FbnAkh5LEs/78xj8= +github.com/reubenmiller/go-c8y v0.11.0-rc.2 h1:d1FWuGGDYvPgcmdw51m8COYJc0xnDtN/8Xqie+KwRZw= +github.com/reubenmiller/go-c8y v0.11.0-rc.2/go.mod h1:aSU0AZdT/IWCn87wMveejGvwQE3FbnAkh5LEs/78xj8= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= diff --git a/pkg/c8ybinary/c8ybinary.go b/pkg/c8ybinary/c8ybinary.go new file mode 100644 index 000000000..f96df8feb --- /dev/null +++ b/pkg/c8ybinary/c8ybinary.go @@ -0,0 +1,234 @@ +package c8ybinary + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "mime" + "net/http" + "os" + "path/filepath" + + "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" + "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/c8y/binary" + "github.com/spf13/cobra" + "github.com/vbauerster/mpb/v6" + "github.com/vbauerster/mpb/v6/decor" +) + +const BarFiller = "[━━ ]" + +// const BarFiller = "[██-]" + +func CreateBinaryWithProgress(ctx context.Context, client *c8y.Client, path string, filename string, properties interface{}, progress *mpb.Progress) (*c8y.Response, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + + values := map[string]io.Reader{ + "file": file, + } + + if properties != nil { + metadataBytes, err := json.Marshal(properties) + if err != nil { + return nil, fmt.Errorf("failed to convert binary properties to json. %w", err) + } + values["object"] = bytes.NewReader(metadataBytes) + } + + return client.SendRequest(ctx, c8y.RequestOptions{ + Method: "POST", + Accept: "application/json", + Path: path, + FormData: values, + PrepareRequest: func(r *http.Request) (*http.Request, error) { + if r.Body == nil || progress == nil { + return r, nil + } + + bar, err := NewProgressBar(progress, filename) + if err != nil { + return nil, err + } + r.Body = bar.ProxyReader(r.Body) + return r, nil + }, + }) +} + +func AddProgress(cmd *cobra.Command, fileFlag string, progress *mpb.Progress) func(r *http.Request) (*http.Request, error) { + return func(r *http.Request) (*http.Request, error) { + if r.Body == nil || progress == nil { + return r, nil + } + filename, err := cmd.Flags().GetString(fileFlag) + if err != nil { + // Don't return error here, as if the user does not provide a file, then ignore the progress + return r, nil + } + + if filename == "" { + return r, nil + } + + bar, err := NewProgressBar(progress, filename) + if err != nil { + return nil, err + } + r.Body = bar.ProxyReader(r.Body) + + return r, nil + } +} + +func CreateProxyReader(progress *mpb.Progress) func(response *http.Response) io.Reader { + return func(r *http.Response) io.Reader { + size := int64(r.ContentLength) + basename := "download" + + _, params, err := mime.ParseMediaType(r.Header.Get("Content-Disposition")) + if err == nil { + if filename, ok := params["filename"]; ok { + basename = filename + } + } + + bar := progress.Add(size, + mpb.NewBarFiller(BarFiller), + mpb.PrependDecorators( + decor.Name("elapsed", decor.WC{W: len("elapsed") + 1, C: decor.DidentRight}), + decor.Elapsed(decor.ET_STYLE_MMSS, decor.WC{W: 8, C: decor.DidentRight}), + decor.Name(basename, decor.WC{W: len(basename) + 1, C: decor.DidentRight}), + ), + mpb.AppendDecorators( + decor.Percentage(decor.WC{W: 6, C: decor.DidentRight}), + decor.CountersKibiByte("% .2f / % .2f"), + ), + ) + + proxyReader := c8y.NewProxyReader(bar.ProxyReader(r.Body)) + r.Body = proxyReader + return proxyReader + } +} + +func NewProgressBar(progress *mpb.Progress, filename string) (*mpb.Bar, error) { + if progress == nil { + return nil, nil + } + + file, err := os.Stat(filename) + + if err != nil { + return nil, err + } + + size := int64(file.Size()) + basename := filepath.Base(filename) + + bar := progress.Add(size, + mpb.NewBarFiller(BarFiller), + mpb.PrependDecorators( + decor.Name("elapsed", decor.WC{W: len("elapsed") + 1, C: decor.DidentRight}), + decor.Elapsed(decor.ET_STYLE_MMSS, decor.WC{W: 8, C: decor.DidentRight}), + decor.Name(basename, decor.WC{W: len(basename) + 1, C: decor.DidentRight}), + ), + mpb.AppendDecorators( + decor.Percentage(decor.WC{W: 6, C: decor.DidentRight}), + decor.CountersKibiByte("% .2f / % .2f"), + ), + ) + + return bar, nil +} + +func NewProxyReader(progress *mpb.Progress, r io.ReadCloser, filename string) (io.ReadCloser, error) { + if progress == nil { + return r, nil + } + + file, err := os.Stat(filename) + + if err != nil { + return nil, err + } + + size := int64(file.Size()) + basename := filepath.Base(filename) + + bar := progress.Add(size, + mpb.NewBarFiller(BarFiller), + mpb.PrependDecorators( + decor.Name("elapsed", decor.WC{W: len("elapsed") + 1, C: decor.DidentRight}), + decor.Elapsed(decor.ET_STYLE_MMSS, decor.WC{W: 8, C: decor.DidentRight}), + decor.Name(basename, decor.WC{W: len(basename) + 1, C: decor.DidentRight}), + ), + mpb.AppendDecorators( + decor.Percentage(decor.WC{W: 6, C: decor.DidentRight}), + decor.CountersKibiByte("% .2f / % .2f"), + ), + ) + + return bar.ProxyReader(r), nil +} + +// WithBinaryUploadURL uploads an inventory binary and returns the URL to it +func WithBinaryUploadURL(client *c8y.Client, progress *mpb.Progress, opts ...string) flags.GetOption { + return func(cmd *cobra.Command, inputIterators *flags.RequestInputIterators) (string, interface{}, error) { + src, dst, _ := flags.UnpackGetterOptions("%s", opts...) + + if !cmd.Flags().Changed(src) { + return "", "", nil + } + + filename, err := cmd.Flags().GetString(src) + if err != nil { + return dst, filename, err + } + + // binary properties + file, err := os.Open(filename) + if err != nil { + return "", nil, err + } + + defer file.Close() + + bar, err := NewProgressBar(progress, filename) + + if err != nil { + return "", nil, err + } + + binaryFile, err := binary.NewBinaryFile( + binary.WithReader(file), + binary.WithFileProperties(filename), + binary.WithGlobal(), + ) + if err != nil { + return "", nil, err + } + + mo, _, err := client.Inventory.CreateBinary(context.Background(), binaryFile, func(r *http.Request) (*http.Request, error) { + if bar != nil { + r.Body = bar.ProxyReader(r.Body) + } + return r, nil + }) + + if progress != nil { + progress.Wait() + } + + if err != nil { + return "", nil, err + } + + return dst, mo.Self, err + } +} diff --git a/pkg/cmd/api/api.manual.go b/pkg/cmd/api/api.manual.go index c80b05130..40df1a844 100644 --- a/pkg/cmd/api/api.manual.go +++ b/pkg/cmd/api/api.manual.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" @@ -278,8 +279,12 @@ func (n *CmdAPI) RunE(cmd *cobra.Command, args []string) error { req.Body = body } + req.PrepareRequest = c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())) // get file info // form data + // BUG: Format data needs to be lazily loaded via the iterator so it can be re-read + // for each request + // formData := make(map[string]io.Reader) err = flags.WithFormDataOptions( cmd, diff --git a/pkg/cmd/applications/createbinary/createBinary.auto.go b/pkg/cmd/applications/createbinary/createBinary.auto.go index a0866ede7..4a1fb0925 100644 --- a/pkg/cmd/applications/createbinary/createBinary.auto.go +++ b/pkg/cmd/applications/createbinary/createBinary.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -160,14 +161,15 @@ func (n *CreateBinaryCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "POST", - Path: path.GetTemplate(), - Query: queryValue, - Body: body, - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/applications/createhostedapplication/createHostedApplication.manual.go b/pkg/cmd/applications/createhostedapplication/createHostedApplication.manual.go index d241f0020..83d02c8ba 100644 --- a/pkg/cmd/applications/createhostedapplication/createHostedApplication.manual.go +++ b/pkg/cmd/applications/createhostedapplication/createHostedApplication.manual.go @@ -13,9 +13,11 @@ import ( "strings" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/completion" "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" "github.com/reubenmiller/go-c8y-cli/v2/pkg/logger" "github.com/reubenmiller/go-c8y-cli/v2/pkg/zipUtilities" @@ -94,6 +96,11 @@ func NewCmdCreateHostedApplication(f *cmdutil.Factory) *CmdCreateHostedApplicati cmd.Flags().BoolVar(&ccmd.skipActivation, "skipActivation", false, "Don't activate to the application after it has been created and uploaded") cmd.Flags().BoolVar(&ccmd.skipUpload, "skipUpload", false, "Don't uploaded the web app binary. Only the application placeholder will be created") + completion.WithOptions( + cmd, + completion.WithValidateSet("availability", "MARKET", "PRIVATE"), + ) + flags.WithOptions( cmd, flags.WithData(), @@ -297,7 +304,16 @@ func (n *CmdCreateHostedApplication) RunE(cmd *cobra.Command, args []string) err } log.Infof("uploading binary [app=%s]", application.ID) - resp, err := client.Application.CreateBinary(context.Background(), zipfile, application.ID) + progress := n.factory.IOStreams.ProgressIndicator() + resp, err := c8ybinary.CreateBinaryWithProgress( + context.Background(), + client, + "/application/applications/"+application.ID+"/binaries", + zipfile, + nil, + progress, + ) + n.factory.IOStreams.WaitForProgressIndicator() if err != nil { // handle error diff --git a/pkg/cmd/binaries/create/create.auto.go b/pkg/cmd/binaries/create/create.auto.go index e64ee86f2..f6b54b1d5 100644 --- a/pkg/cmd/binaries/create/create.auto.go +++ b/pkg/cmd/binaries/create/create.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmdutil" @@ -160,14 +161,15 @@ func (n *CreateCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "POST", - Path: path.GetTemplate(), - Query: queryValue, - Body: body, - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/binaries/update/update.auto.go b/pkg/cmd/binaries/update/update.auto.go index 7d5d11725..4fb7224b3 100644 --- a/pkg/cmd/binaries/update/update.auto.go +++ b/pkg/cmd/binaries/update/update.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -150,14 +151,15 @@ func (n *UpdateCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "PUT", - Path: path.GetTemplate(), - Query: queryValue, - Body: body.GetFileContents(), - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "PUT", + Path: path.GetTemplate(), + Query: queryValue, + Body: body.GetFileContents(), + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/configuration/create/create.auto.go b/pkg/cmd/configuration/create/create.auto.go index 874faaf22..988c00841 100644 --- a/pkg/cmd/configuration/create/create.auto.go +++ b/pkg/cmd/configuration/create/create.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ydata" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -147,7 +148,7 @@ func (n *CreateCmd) RunE(cmd *cobra.Command, args []string) error { flags.WithStringValue("configurationType", "configurationType"), flags.WithStringValue("url", "url"), flags.WithStringValue("deviceType", "deviceType"), - flags.WithBinaryUploadURL(client, "file", "url"), + c8ybinary.WithBinaryUploadURL(client, cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY()), "file", "url"), flags.WithDefaultTemplateString(` {type: 'c8y_ConfigurationDump', c8y_Global:{}}`), cmdutil.WithTemplateValue(cfg), diff --git a/pkg/cmd/configuration/update/update.auto.go b/pkg/cmd/configuration/update/update.auto.go index 0bebbd341..6be7523ce 100644 --- a/pkg/cmd/configuration/update/update.auto.go +++ b/pkg/cmd/configuration/update/update.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ydata" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" @@ -143,7 +144,7 @@ func (n *UpdateCmd) RunE(cmd *cobra.Command, args []string) error { flags.WithStringValue("configurationType", "configurationType"), flags.WithStringValue("url", "url"), flags.WithStringValue("deviceType", "deviceType"), - flags.WithBinaryUploadURL(client, "file", "url"), + c8ybinary.WithBinaryUploadURL(client, n.factory.IOStreams.ProgressIndicator(), "file", "url"), cmdutil.WithTemplateValue(cfg), flags.WithTemplateVariablesValue(), ) diff --git a/pkg/cmd/events/createbinary/createBinary.auto.go b/pkg/cmd/events/createbinary/createBinary.auto.go index 4477137c8..f7aa4d64e 100644 --- a/pkg/cmd/events/createbinary/createBinary.auto.go +++ b/pkg/cmd/events/createbinary/createBinary.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -158,14 +159,15 @@ func (n *CreateBinaryCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "POST", - Path: path.GetTemplate(), - Query: queryValue, - Body: body, - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/events/updatebinary/updateBinary.auto.go b/pkg/cmd/events/updatebinary/updateBinary.auto.go index 1ac29683e..718e15206 100644 --- a/pkg/cmd/events/updatebinary/updateBinary.auto.go +++ b/pkg/cmd/events/updatebinary/updateBinary.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -150,14 +151,15 @@ func (n *UpdateBinaryCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "PUT", - Path: path.GetTemplate(), - Query: queryValue, - Body: body.GetFileContents(), - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "PUT", + Path: path.GetTemplate(), + Query: queryValue, + Body: body.GetFileContents(), + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/firmware/patches/create/create.manual.go b/pkg/cmd/firmware/patches/create/create.manual.go index dfd9a1eb5..139b2f564 100644 --- a/pkg/cmd/firmware/patches/create/create.manual.go +++ b/pkg/cmd/firmware/patches/create/create.manual.go @@ -4,8 +4,10 @@ import ( "context" "io" "net/http" + "os" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -14,6 +16,7 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/c8y/binary" "github.com/spf13/cobra" ) @@ -190,10 +193,38 @@ func (n *CreateCmd) RunE(cmd *cobra.Command, args []string) error { if filename == "" { _, resp, respErr = client.Inventory.CreateChildAddition(context.Background(), firmwareID, body) } else { + + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + progress := n.factory.IOStreams.ProgressIndicator() + bar, err := c8ybinary.NewProgressBar(progress, filename) + + if err != nil { + return err + } + + binaryFile, err := binary.NewBinaryFile( + binary.WithReader(file), + binary.WithFileProperties(filename), + binary.WithGlobal(), + ) + + if err != nil { + return err + } _, resp, respErr = client.Inventory.CreateChildAdditionWithBinary( - context.Background(), firmwareID, filename, func(binaryURL string) interface{} { + context.Background(), firmwareID, binaryFile, func(binaryURL string) interface{} { _ = body.Set("c8y_Firmware.url", binaryURL) return body + }, func(r *http.Request) (*http.Request, error) { + if bar != nil { + r.Body = bar.ProxyReader(r.Body) + } + return r, nil }) } diff --git a/pkg/cmd/firmware/versions/create/create.manual.go b/pkg/cmd/firmware/versions/create/create.manual.go index e685a56e2..a06398f6a 100644 --- a/pkg/cmd/firmware/versions/create/create.manual.go +++ b/pkg/cmd/firmware/versions/create/create.manual.go @@ -4,8 +4,10 @@ import ( "context" "io" "net/http" + "os" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -14,6 +16,7 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/c8y/binary" "github.com/spf13/cobra" ) @@ -182,10 +185,37 @@ func (n *CreateCmd) RunE(cmd *cobra.Command, args []string) error { if filename == "" { _, resp, respErr = client.Inventory.CreateChildAddition(context.Background(), firmwareID, body) } else { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + progress := n.factory.IOStreams.ProgressIndicator() + bar, err := c8ybinary.NewProgressBar(progress, filename) + + if err != nil { + return err + } + + binaryFile, err := binary.NewBinaryFile( + binary.WithReader(file), + binary.WithFileProperties(filename), + binary.WithGlobal(), + ) + + if err != nil { + return err + } _, resp, respErr = client.Inventory.CreateChildAdditionWithBinary( - context.Background(), firmwareID, filename, func(binaryURL string) interface{} { + context.Background(), firmwareID, binaryFile, func(binaryURL string) interface{} { _ = body.Set("c8y_Firmware.url", binaryURL) return body + }, func(r *http.Request) (*http.Request, error) { + if bar != nil { + r.Body = bar.ProxyReader(r.Body) + } + return r, nil }) } diff --git a/pkg/cmd/microservices/create/create.manual.go b/pkg/cmd/microservices/create/create.manual.go index b55031999..3b41d9ff5 100644 --- a/pkg/cmd/microservices/create/create.manual.go +++ b/pkg/cmd/microservices/create/create.manual.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -261,7 +262,17 @@ func (n *CmdCreate) RunE(cmd *cobra.Command, args []string) error { if !skipUpload { log.Infof("uploading binary [id=%s]", application.ID) if !dryRun { - _, err := client.Application.CreateBinary(context.Background(), n.file, application.ID) + + progress := n.factory.IOStreams.ProgressIndicator() + _, err := c8ybinary.CreateBinaryWithProgress( + context.Background(), + client, + "/application/applications/"+application.ID+"/binaries", + n.file, + nil, + progress, + ) + n.factory.IOStreams.WaitForProgressIndicator() if err != nil { // handle error diff --git a/pkg/cmd/microservices/createbinary/createBinary.auto.go b/pkg/cmd/microservices/createbinary/createBinary.auto.go index ab1c4fcd8..3d0b0ae43 100644 --- a/pkg/cmd/microservices/createbinary/createBinary.auto.go +++ b/pkg/cmd/microservices/createbinary/createBinary.auto.go @@ -6,6 +6,7 @@ import ( "net/http" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -161,14 +162,15 @@ func (n *CreateBinaryCmd) RunE(cmd *cobra.Command, args []string) error { } req := c8y.RequestOptions{ - Method: "POST", - Path: path.GetTemplate(), - Query: queryValue, - Body: body, - FormData: formData, - Header: headers, - IgnoreAccept: cfg.IgnoreAcceptHeader(), - DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + Method: "POST", + Path: path.GetTemplate(), + Query: queryValue, + Body: body, + FormData: formData, + Header: headers, + IgnoreAccept: cfg.IgnoreAcceptHeader(), + DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + PrepareRequest: c8ybinary.AddProgress(cmd, "file", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())), } return n.factory.RunWithWorkers(client, cmd, &req, inputIterators) diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go index 48679cf38..99af3a8e3 100644 --- a/pkg/cmd/root/root.go +++ b/pkg/cmd/root/root.go @@ -185,6 +185,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) *CmdRoot { cmd.PersistentFlags().BoolP("raw", "r", false, "Show raw response. This mode will force output=json and view=off") cmd.PersistentFlags().String("proxy", "", "Proxy setting, i.e. http://10.0.0.1:8080") cmd.PersistentFlags().Bool("noProxy", false, "Ignore the proxy settings") + cmd.PersistentFlags().Bool("noProgress", false, "Disable progress bars") cmd.PersistentFlags().Bool("withError", false, "Errors will be printed on stdout instead of stderr") cmd.PersistentFlags().StringSliceP("header", "H", nil, `custom headers. i.e. --header "Accept: value, AnotherHeader: myvalue"`) cmd.PersistentFlags().StringSlice("customQueryParam", nil, `add custom URL query parameters. i.e. --customQueryParam 'withCustomOption=true,myOtherOption=myvalue'`) @@ -438,6 +439,11 @@ func (c *CmdRoot) Configure(disableEncryptionCheck bool) error { } } + if cfg.DisableProgress() { + log.Debugf("Disabling progress bars") + c.Factory.IOStreams.SetProgress(false) + } + // // Update cmd factory before passing it along // diff --git a/pkg/cmd/settings/update/update.manual.go b/pkg/cmd/settings/update/update.manual.go index dec4cf49c..0d4acba16 100644 --- a/pkg/cmd/settings/update/update.manual.go +++ b/pkg/cmd/settings/update/update.manual.go @@ -198,6 +198,12 @@ var updateSettingsOptions = map[string]argumentHandler{ "false", }, nil, cobra.ShellCompDirectiveNoFileComp}, + // no progress + "defaults.noProgress": {"defaults.noProgress", "bool", "settings.defaults.noProgress", []string{ + "true", + "false", + }, nil, cobra.ShellCompDirectiveNoFileComp}, + // withError "defaults.withError": {"defaults.withError", "bool", "settings.defaults.withError", []string{ "true", diff --git a/pkg/cmd/software/versions/create/create.manual.go b/pkg/cmd/software/versions/create/create.manual.go index fc865b86e..93abe7ca6 100644 --- a/pkg/cmd/software/versions/create/create.manual.go +++ b/pkg/cmd/software/versions/create/create.manual.go @@ -4,8 +4,10 @@ import ( "context" "io" "net/http" + "os" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -14,6 +16,7 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/flags" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" "github.com/reubenmiller/go-c8y/pkg/c8y" + "github.com/reubenmiller/go-c8y/pkg/c8y/binary" "github.com/spf13/cobra" ) @@ -182,10 +185,37 @@ func (n *CreateCmd) RunE(cmd *cobra.Command, args []string) error { if filename == "" { _, resp, respErr = client.Inventory.CreateChildAddition(context.Background(), softwareID, body) } else { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + progress := n.factory.IOStreams.ProgressIndicator() + bar, err := c8ybinary.NewProgressBar(progress, filename) + + if err != nil { + return err + } + + binaryFile, err := binary.NewBinaryFile( + binary.WithReader(file), + binary.WithFileProperties(filename), + binary.WithGlobal(), + ) + + if err != nil { + return err + } _, resp, respErr = client.Inventory.CreateChildAdditionWithBinary( - context.Background(), softwareID, filename, func(binaryURL string) interface{} { + context.Background(), softwareID, binaryFile, func(binaryURL string) interface{} { _ = body.Set("c8y_Software.url", binaryURL) return body + }, func(r *http.Request) (*http.Request, error) { + if bar != nil { + r.Body = bar.ProxyReader(r.Body) + } + return r, nil }) } diff --git a/pkg/config/cliConfiguration.go b/pkg/config/cliConfiguration.go index dee3681b1..a6160e964 100644 --- a/pkg/config/cliConfiguration.go +++ b/pkg/config/cliConfiguration.go @@ -3,6 +3,7 @@ package config import ( "errors" "fmt" + "io" "io/ioutil" "os" "path" @@ -25,6 +26,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" + "github.com/vbauerster/mpb/v6" ) var ( @@ -118,6 +120,9 @@ const ( // SettingsShowProgress show progress bar SettingsShowProgress = "settings.defaults.progress" + // SettingsDisableColor don't print progress bar + SettingsDisableProgress = "settings.defaults.noProgress" + // SettingsDisableColor don't print console output in color SettingsDisableColor = "settings.defaults.noColor" @@ -1026,7 +1031,22 @@ func (c *Config) CompactJSON() bool { // ShowProgress show progress bar func (c *Config) ShowProgress() bool { - return c.viper.GetBool(SettingsShowProgress) + return c.viper.GetBool(SettingsShowProgress) && !c.DisableProgress() +} + +func (c *Config) GetProgressBar(w io.Writer, enable bool) (progress *mpb.Progress) { + if enable && !c.DisableProgress() { + progress = mpb.New( + mpb.WithOutput(w), + mpb.WithRefreshRate(180*time.Millisecond), + ) + } + return +} + +// DisableProgress don't print progress bar +func (c *Config) DisableProgress() bool { + return c.viper.GetBool(SettingsDisableProgress) } // DisableColor don't print console output in color diff --git a/pkg/flags/getters.go b/pkg/flags/getters.go index 5ee8b08f5..e84e55a66 100644 --- a/pkg/flags/getters.go +++ b/pkg/flags/getters.go @@ -1,17 +1,14 @@ package flags import ( - "context" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "log" - "mime" "net/http" "os" - "path/filepath" "strings" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yquery" @@ -21,7 +18,6 @@ import ( "github.com/reubenmiller/go-c8y-cli/v2/pkg/iterator" "github.com/reubenmiller/go-c8y-cli/v2/pkg/jsonUtilities" "github.com/reubenmiller/go-c8y-cli/v2/pkg/mapbuilder" - "github.com/reubenmiller/go-c8y/pkg/c8y" "github.com/spf13/cobra" ) @@ -815,40 +811,6 @@ func WithDataValueAdvanced(stripCumulocityKeys bool, raw bool, opts ...string) G } } -// WithBinaryUploadURL uploads an inventory binary and returns the URL to it -func WithBinaryUploadURL(client *c8y.Client, opts ...string) GetOption { - return func(cmd *cobra.Command, inputIterators *RequestInputIterators) (string, interface{}, error) { - - src, dst, _ := UnpackGetterOptions("%s", opts...) - - if !cmd.Flags().Changed(src) { - return "", "", nil - } - - value, err := cmd.Flags().GetString(src) - if err != nil { - return dst, value, err - } - - // binary properties - binaryProps := make(map[string]interface{}) - binaryProps["c8y_Global"] = map[string]interface{}{} - binaryProps["name"] = filepath.Base(value) - mimeType := mime.TypeByExtension(filepath.Ext(value)) - if mimeType == "" { - mimeType = "application/octet-stream" - } - binaryProps["type"] = mimeType - mo, _, err := client.Inventory.CreateBinary(context.Background(), value, binaryProps) - - if err != nil { - return "", nil, err - } - - return dst, mo.Self, err - } -} - // WithCumulocityQuery build a Cumulocity Query Expression func WithCumulocityQuery(queryOptions []GetOption, opts ...string) GetOption { return func(cmd *cobra.Command, inputIterators *RequestInputIterators) (string, interface{}, error) { diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index 0a65c61e7..a92644895 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -9,12 +9,14 @@ import ( "os/exec" "strconv" "strings" + "time" "github.com/cli/safeexec" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/muesli/termenv" "github.com/reubenmiller/go-c8y-cli/v2/pkg/logger" + "github.com/vbauerster/mpb/v6" "golang.org/x/term" ) @@ -43,6 +45,13 @@ type IOStreams struct { neverPrompt bool TempFileOverride *os.File + + progress *mpb.Progress +} + +func (s *IOStreams) SetProgress(v bool) { + s.progressIndicatorEnabled = v + s.progress = nil } func (s *IOStreams) SetColor(v bool) { @@ -197,6 +206,24 @@ func (s *IOStreams) TempFile(dir, pattern string) (*os.File, error) { return ioutil.TempFile(dir, pattern) } +func (s *IOStreams) ProgressIndicator() *mpb.Progress { + if s.progressIndicatorEnabled { + if s.progress == nil { + s.progress = mpb.New( + mpb.WithOutput(s.ErrOut), + mpb.WithRefreshRate(180*time.Millisecond), + ) + } + } + return s.progress +} + +func (s *IOStreams) WaitForProgressIndicator() { + if s.progress != nil { + s.progress.Wait() + } +} + func System(colorDisabled bool, colorForced bool) *IOStreams { stdoutIsTTY := isTerminal(os.Stdout) stderrIsTTY := isTerminal(os.Stderr) @@ -210,7 +237,7 @@ func System(colorDisabled bool, colorForced bool) *IOStreams { is256enabled: Is256ColorSupported(), } - if stdoutIsTTY && stderrIsTTY { + if stderrIsTTY { io.progressIndicatorEnabled = true } diff --git a/pkg/request/request.go b/pkg/request/request.go index 0dfd18ac4..80c407640 100644 --- a/pkg/request/request.go +++ b/pkg/request/request.go @@ -21,6 +21,7 @@ import ( "github.com/fatih/color" "github.com/reubenmiller/go-c8y-cli/v2/pkg/activitylogger" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" "github.com/reubenmiller/go-c8y-cli/v2/pkg/config" "github.com/reubenmiller/go-c8y-cli/v2/pkg/console" @@ -93,6 +94,16 @@ func (r *RequestHandler) ProcessRequestAndResponse(requests []c8y.RequestOptions c8y.GetContextCommonOptionsKey(), c8y.CommonOptions{ DryRun: req.DryRun, + OnResponse: func(response *http.Response) io.Reader { + // Add progress bar for binary downloads + prog := r.IO.ProgressIndicator() + if prog != nil && response.Header.Get("Content-Disposition") != "" { + if response.ContentLength > 0 { + return c8ybinary.CreateProxyReader(prog)(response) + } + } + return response.Body + }, }) defer cancel() start := time.Now() @@ -832,6 +843,10 @@ func (r *RequestHandler) ProcessResponse(resp *c8y.Response, respError error, co responseText = bytes.ReplaceAll(responseText, []byte("\\u003e"), []byte(">")) responseText = bytes.ReplaceAll(responseText, []byte("\\u0026"), []byte("&")) + // Wait for progress bar to finish before printing to console + // to prevent overriding the output + r.IO.WaitForProgressIndicator() + consol := r.Console if respError == nil { jsonformatter.WithOutputFormatters( @@ -963,7 +978,6 @@ func (r *RequestHandler) saveResponseToFile(resp *c8y.Response, filename string, // Writer the body to file r.Logger.Printf("header: %v", resp.Header()) fmt.Fprintf(out, "%s", resp.Body()) - // _, err = io.Copy(out, resp.Body()) if err != nil { return "", fmt.Errorf("failed to copy file contents to file. %s", err) diff --git a/pkg/requestiterator/requestiterator.go b/pkg/requestiterator/requestiterator.go index 66f2c0b58..625bd1370 100644 --- a/pkg/requestiterator/requestiterator.go +++ b/pkg/requestiterator/requestiterator.go @@ -72,6 +72,7 @@ func (r *RequestIterator) GetNext() (*c8y.RequestOptions, interface{}, error) { NoAuthentication: r.Request.NoAuthentication, IgnoreAccept: r.Request.IgnoreAccept, DryRun: r.Request.DryRun, + PrepareRequest: r.Request.PrepareRequest, } var inputLine interface{} diff --git a/scripts/build-cli/New-C8yApiGoCommand.ps1 b/scripts/build-cli/New-C8yApiGoCommand.ps1 index 9d2348d27..faba3bf92 100644 --- a/scripts/build-cli/New-C8yApiGoCommand.ps1 +++ b/scripts/build-cli/New-C8yApiGoCommand.ps1 @@ -254,6 +254,9 @@ $null = $PostActionOptions.AppendLine("}") } + # Prepare Request + $PrepareRequest = New-Object System.Text.StringBuilder + # # Body # @@ -275,7 +278,16 @@ } } + $HasProgress = $false foreach ($iArg in (Remove-SkippedParameters $Specification.body)) { + + if ($Specification.method -match "POST|PUT" -and -Not $HasProgress) { + if ($iArg.type -in @("file", "fileContents", "attachment")) { + $HasProgress = $true + $null = $PrepareRequest.Append("PrepareRequest: c8ybinary.AddProgress(cmd, `"$($iArg.name)`", cfg.GetProgressBar(n.factory.IOStreams.ErrOut, n.factory.IOStreams.IsStderrTTY())),") + } + } + $code = New-C8yApiGoGetValueFromFlag -Parameters $iArg -SetterType "body" if ($code) { switch -Regex ($code) { @@ -284,7 +296,7 @@ break } - "^(flags\.|c8yfetcher\.|With)" { + "^(flags\.|c8yfetcher\.|With|c8ybinary\.)" { $null = $RESTBodyBuilderOptions.AppendLine($code) break } @@ -498,6 +510,7 @@ import ( "net/url" "github.com/MakeNowJust/heredoc/v2" + "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8ybinary" "github.com/reubenmiller/go-c8y-cli/v2/pkg/c8yfetcher" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmd/subcommand" "github.com/reubenmiller/go-c8y-cli/v2/pkg/cmderrors" @@ -678,6 +691,7 @@ func (n *${NameCamel}Cmd) RunE(cmd *cobra.Command, args []string) error { Header: headers, IgnoreAccept: cfg.IgnoreAcceptHeader(), DryRun: cfg.ShouldUseDryRun(cmd.CommandPath()), + $PrepareRequest } $PostActionOptions @@ -710,12 +724,12 @@ Remove skipped parameters. These are parameter which should not be used when gen [object[]] $CommandParameters ) - $CommandParameters | Where-Object { + [array]($CommandParameters | Where-Object { if ($_.skip -eq $true) { Write-Verbose ("Skipping parameter [{0}] as it is marked as skip" -f $_.name) } $_.skip -ne $true - } + }) } Function Get-C8yGoArgs { diff --git a/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 b/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 index c23814f08..b059eb33a 100644 --- a/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 +++ b/scripts/build-cli/New-C8yApiGoGetValueFromFlag.ps1 @@ -82,7 +82,7 @@ "json_custom" = "flags.WithDataValue(`"${prop}`", `"${queryParam}`"$FormatValue)," # binaryUploadURL: uploads a binary and returns the URL - "binaryUploadURL" = "flags.WithBinaryUploadURL(client, `"${prop}`", `"${queryParam}`"$FormatValue)," + "binaryUploadURL" = "c8ybinary.WithBinaryUploadURL(client, n.factory.IOStreams.ProgressIndicator(), `"${prop}`", `"${queryParam}`"$FormatValue)," # json - don't do anything because it should be manually set "json" = "" diff --git a/tests/manual/api/api.yaml b/tests/manual/api/api.yaml index e00be284c..51fba44c0 100644 --- a/tests/manual/api/api.yaml +++ b/tests/manual/api/api.yaml @@ -185,3 +185,8 @@ tests: stdout: not-contains: - is not a valid date + + It creates an inventory via custom command: + command: | + c8y api POST /inventory/managedObjects --data name=test --select id,name --dry=false | c8y inventory delete + exit-code: 0 diff --git a/tools/PSc8y/Public-manual/Get-ClientCommonParameters.ps1 b/tools/PSc8y/Public-manual/Get-ClientCommonParameters.ps1 index 19df9bb66..ea6398250 100644 --- a/tools/PSc8y/Public-manual/Get-ClientCommonParameters.ps1 +++ b/tools/PSc8y/Public-manual/Get-ClientCommonParameters.ps1 @@ -83,6 +83,7 @@ Inherit common parameters to a custom function. This will add parameters such as New-DynamicParam -Name Proxy -Type "switch" -DPDictionary $Dictionary -HelpMessage "Proxy setting, i.e. http://10.0.0.1:8080" New-DynamicParam -Name NoProxy -Type "switch" -DPDictionary $Dictionary -HelpMessage "Ignore the proxy settings" New-DynamicParam -Name Timeout -Type "string" -DPDictionary $Dictionary -HelpMessage "Request timeout. It accepts a duration, i.e. 1ms, 0.5s, 1m etc." + New-DynamicParam -Name NoProgress -Type "switch" -DPDictionary $Dictionary -HelpMessage "Disable progress bars" # Session New-DynamicParam -Name Session -Type "string" -DPDictionary $Dictionary -HelpMessage "Session configuration" diff --git a/tools/PSc8y/Tests/New-HostedApplication.manual.Tests.ps1 b/tools/PSc8y/Tests/New-HostedApplication.manual.Tests.ps1 index 28fece9b5..085a53597 100644 --- a/tools/PSc8y/Tests/New-HostedApplication.manual.Tests.ps1 +++ b/tools/PSc8y/Tests/New-HostedApplication.manual.Tests.ps1 @@ -110,7 +110,7 @@ Describe -Name "New-HostedApplication" { $webResponse | Out-String | Should -BeLike "*Hi there. This is a test web application*" } - It -Tag "TODO" "Uploads new applicaiton but does not activate it" { + It -Tag "TODO" "Uploads new application but does not activate it" { $application = Get-Application -Id $AppName $application | Should -Not -BeNullOrEmpty $application.name | Should -BeExactly $AppName