Skip to content

Commit

Permalink
'describe attachment'
Browse files Browse the repository at this point in the history
  • Loading branch information
flimzy committed Dec 2, 2020
1 parent d35a846 commit a8627db
Show file tree
Hide file tree
Showing 15 changed files with 243 additions and 10 deletions.
18 changes: 11 additions & 7 deletions cmd/kouchctl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,27 @@ import (
)

type describe struct {
doc *cobra.Command
db *cobra.Command
ver *cobra.Command
att, doc, db, ver *cobra.Command
*root
}

func descrCmd(r *root) *cobra.Command {
g := &describe{
root: r,
att: descrAttachmentCmd(r),
doc: descrDocCmd(r),
db: descrDBCmd(r),
ver: descrVerCmd(r),
}
cmd := &cobra.Command{
Use: "describe [command]",
Short: "Describe a resource",
Long: `Describe a resource described by the URL`,
RunE: g.RunE,
Use: "describe [command]",
Aliases: []string{"desc", "descr"},
Short: "Describe a resource",
Long: `Describe a resource described by the URL`,
RunE: g.RunE,
}

cmd.AddCommand(g.att)
cmd.AddCommand(g.doc)
cmd.AddCommand(g.db)
cmd.AddCommand(g.ver)
Expand All @@ -45,6 +46,9 @@ func descrCmd(r *root) *cobra.Command {
}

func (g *describe) RunE(cmd *cobra.Command, args []string) error {
if g.conf.HasAttachment() {
return g.att.RunE(cmd, args)
}
if g.conf.HasDoc() {
return g.doc.RunE(cmd, args)
}
Expand Down
67 changes: 67 additions & 0 deletions cmd/kouchctl/cmd/describe_attachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 cmd

import (
"github.com/spf13/cobra"

"github.com/go-kivik/xkivik/v4/cmd/kouchctl/output"
)

type descrAttachment struct {
*root
}

func descrAttachmentCmd(r *root) *cobra.Command {
c := &descrAttachment{
root: r,
}
return &cobra.Command{
Use: "attachment [dsn]/[database]/[document]/[filename]",
Aliases: []string{"att", "attach"},
Short: "Describe an attachment",
Long: `Fetch attachment with the HTTP HEAD verb`,
RunE: c.RunE,
}
}

func (c *descrAttachment) RunE(cmd *cobra.Command, _ []string) error {
client, err := c.client()
if err != nil {
return err
}
db, docID, filename, err := c.conf.DBDocFilename()
if err != nil {
return err
}
c.log.Debugf("[get] Will fetch document: %s/%s/%s", client.DSN(), db, docID)
return c.retry(func() error {
att, err := client.DB(db).GetAttachmentMeta(cmd.Context(), docID, filename, c.opts())
if err != nil {
return err
}
att.Filename = filename

format := `Filename: {{ .Filename }}
Content-Type: {{ .ContentType }}
Content-Length: {{ .Size }}
Digest: {{ .Digest }}
{{- with .RevPos }}
RevPos: {{ . }}
{{ end -}}
`

result := output.TemplateReader(format, att, output.JSONReader(att))
return c.fmt.Output(result)
})
}
82 changes: 82 additions & 0 deletions cmd/kouchctl/cmd/describe_attachment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 cmd

import (
"io/ioutil"
"net/http"
"strings"
"testing"

"gitlab.com/flimzy/testy"

"github.com/go-kivik/xkivik/v4/cmd/kouchctl/errors"
)

func Test_describe_attachment_RunE(t *testing.T) {
tests := testy.NewTable()

tests.Add("missing resource", cmdTest{
args: []string{"descr", "attachment"},
status: errors.ErrUsage,
})
tests.Add("not found", func(t *testing.T) interface{} {
s := testy.ServeResponse(&http.Response{
StatusCode: http.StatusNotFound,
Header: http.Header{
"Content-Type": []string{"application/json"},
},
Body: ioutil.NopCloser(strings.NewReader(`{"error":"not_found","reason":"Document is missing attachment"}
`)),
})

return cmdTest{
args: []string{"descr", "attachment", s.URL + "/db/doc/foo.txt"},
status: errors.ErrNotFound,
}
})
tests.Add("success", func(t *testing.T) interface{} {
s := testy.ServeResponse(&http.Response{
StatusCode: http.StatusOK,
Header: http.Header{
"Content-Type": []string{"text/plain"},
"Server": []string{"CouchDB/2.3.1 (Erlang OTP/20)"},
"ETag": []string{`"cy5z3SF7yaYp4vmLX0k31Q==`},
},
Body: ioutil.NopCloser(strings.NewReader(`Testing`)),
})

return cmdTest{
args: []string{"descr", "attachment", s.URL + "/db/doc/foo.txt"},
}
})
tests.Add("success json", func(t *testing.T) interface{} {
s := testy.ServeResponse(&http.Response{
StatusCode: http.StatusOK,
Header: http.Header{
"Content-Type": []string{"text/plain"},
"Server": []string{"CouchDB/2.3.1 (Erlang OTP/20)"},
"ETag": []string{`"cy5z3SF7yaYp4vmLX0k31Q==`},
},
Body: ioutil.NopCloser(strings.NewReader(`Testing`)),
})

return cmdTest{
args: []string{"descr", "attachment", s.URL + "/db/doc/foo.txt", "-f", "json"},
}
})

tests.Run(t, func(t *testing.T, tt cmdTest) {
tt.Test(t)
})
}
15 changes: 15 additions & 0 deletions cmd/kouchctl/cmd/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ func Test_describe_RunE(t *testing.T) {
args: []string{"describe", s.URL},
}
})
tests.Add("auto attachment", func(t *testing.T) interface{} {
s := testy.ServeResponse(&http.Response{
StatusCode: http.StatusOK,
Header: http.Header{
"Content-Type": []string{"text/plain"},
"Server": []string{"CouchDB/2.3.1 (Erlang OTP/20)"},
"ETag": []string{`"cy5z3SF7yaYp4vmLX0k31Q==`},
},
Body: ioutil.NopCloser(strings.NewReader(`Testing`)),
})

return cmdTest{
args: []string{"descr", s.URL + "/db/doc/foo.txt"},
}
})

tests.Run(t, func(t *testing.T, tt cmdTest) {
tt.Test(t)
Expand Down
3 changes: 3 additions & 0 deletions cmd/kouchctl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func getCmd(r *root) *cobra.Command {
}

func (g *get) RunE(cmd *cobra.Command, args []string) error {
if g.conf.HasAttachment() {
return g.att.RunE(cmd, args)
}
if g.conf.HasDoc() {
return g.doc.RunE(cmd, args)
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/kouchctl/cmd/get_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func getAttachmentCmd(r *root) *cobra.Command {
root: r,
}
return &cobra.Command{
Use: "attachment [dsn]/[database]/[document]/[filename",
Use: "attachment [dsn]/[database]/[document]/[filename]",
Aliases: []string{"att", "attach"},
Short: "Get an attachment",
Long: `Fetch an attachment with the HTTP GET verb`,
Expand Down Expand Up @@ -79,5 +79,9 @@ func (a *attachment) Read(p []byte) (int, error) {
if a.Reader == nil {
a.Reader = output.JSONReader(a)
}
return a.Reader.Read(p)
n, err := a.Reader.Read(p)
if err == io.EOF {
_ = a.Content.Close()
}
return n, err
}
15 changes: 15 additions & 0 deletions cmd/kouchctl/cmd/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ func Test_get_RunE(t *testing.T) {
args: []string{"get", s.URL},
}
})
tests.Add("auto attachment", func(t *testing.T) interface{} {
s := testy.ServeResponse(&http.Response{
StatusCode: http.StatusOK,
Header: http.Header{
"Content-Type": []string{"text/plain"},
"Server": []string{"CouchDB/2.3.1 (Erlang OTP/20)"},
"ETag": []string{`"cy5z3SF7yaYp4vmLX0k31Q==`},
},
Body: ioutil.NopCloser(strings.NewReader(`Testing`)),
})

return cmdTest{
args: []string{"get", s.URL + "/db/doc/foo.txt"},
}
})

tests.Run(t, func(t *testing.T, tt cmdTest) {
tt.Test(t)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Filename: foo.txt
Content-Type: text/plain
Content-Length: 7
Digest: cy5z3SF7yaYp4vmLX0k31Q==
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Error: no context specified
Usage:
kouchctl describe attachment [dsn]/[database]/[document]/[filename] [flags]

Aliases:
attachment, att, attach

Flags:
-h, --help help for attachment

Global Flags:
--connect-timeout string Limits the time spent establishing a TCP connection.
--debug Enable debug output
-f, --format string Output format. One of: json[=...]|raw|yaml|go-template=...
-H, --header Output response header
--kouchconfig string Path to kouchconfig file to use for CLI requests (default "~/.kouchctl/config")
-O, --option stringToString CouchDB options, specified as key=value. May be repeated. (default [])
-o, --output string Output file/directory.
-F, --overwrite Overwrite output file
--request-timeout string The time limit for each request.
--retry int In case of transient error, retry up to this many times. A negative value retries forever.
--retry-delay string Delay between retry attempts. Disables the default exponential backoff algorithm.
--retry-timeout string When used with --retry, no more retries will be attempted after this timeout.
-v, --verbose Output bi-directional network traffic

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Error: Not Found
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Filename: foo.txt
Content-Type: text/plain
Content-Length: 7
Digest: cy5z3SF7yaYp4vmLX0k31Q==
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"content_type": "text/plain",
"digest": "cy5z3SF7yaYp4vmLX0k31Q==",
"length": 7
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Testing
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Error: no context specified
Usage:
kouchctl get attachment [dsn]/[database]/[document]/[filename [flags]
kouchctl get attachment [dsn]/[database]/[document]/[filename] [flags]

Aliases:
attachment, att, attach
Expand Down
3 changes: 3 additions & 0 deletions cmd/kouchctl/output/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@ func TemplateReader(tmpl string, data interface{}, r io.Reader) FriendlyOutput {
}

func (t *tmplReader) Execute(w io.Writer) error {
if rc, ok := t.Reader.(io.ReadCloser); ok {
defer rc.Close() // nolint:errcheck
}
return t.tmpl.Execute(w, t.data)
}

0 comments on commit a8627db

Please sign in to comment.