diff --git a/client/go/internal/cli/cmd/document.go b/client/go/internal/cli/cmd/document.go index 267f0e91c291..6ff85fd27e74 100644 --- a/client/go/internal/cli/cmd/document.go +++ b/client/go/internal/cli/cmd/document.go @@ -101,10 +101,10 @@ func sendOperation(op document.Operation, args []string, timeoutSecs int, waiter service.CurlWriter.InputFile = filename } result := client.Send(doc) - return printResult(cli, operationResult(false, doc, service, result), false) + return printResult(cli, operationResult(false, doc, service, result), false, false) } -func readDocuments(ids []string, timeoutSecs int, waiter *Waiter, printCurl bool, cli *CLI, fieldSet string, headers []string) error { +func readDocuments(ids []string, timeoutSecs int, waiter *Waiter, printCurl bool, cli *CLI, fieldSet string, headers []string, ignoreMissing bool) error { parsedIds := make([]document.Id, 0, len(ids)) for _, id := range ids { parsedId, err := document.ParseId(id) @@ -121,7 +121,7 @@ func readDocuments(ids []string, timeoutSecs int, waiter *Waiter, printCurl bool for _, docId := range parsedIds { result := client.Get(docId, fieldSet) - if err := printResult(cli, operationResult(true, document.Document{Id: docId}, service, result), true); err != nil { + if err := printResult(cli, operationResult(true, document.Document{Id: docId}, service, result), true, ignoreMissing); err != nil { return err } } @@ -266,7 +266,7 @@ $ vespa document remove id:mynamespace:music::a-head-full-of-dreams`, } doc := document.Document{Id: id, Operation: document.OperationRemove} result := client.Send(doc) - return printResult(cli, operationResult(false, doc, service, result), false) + return printResult(cli, operationResult(false, doc, service, result), false, false) } else { return sendOperation(document.OperationRemove, args, timeoutSecs, waiter, printCurl, cli, headers) } @@ -278,11 +278,12 @@ $ vespa document remove id:mynamespace:music::a-head-full-of-dreams`, func newDocumentGetCmd(cli *CLI) *cobra.Command { var ( - printCurl bool - timeoutSecs int - waitSecs int - fieldSet string - headers []string + printCurl bool + ignoreMissing bool + timeoutSecs int + waitSecs int + fieldSet string + headers []string ) cmd := &cobra.Command{ Use: "get id", @@ -293,10 +294,11 @@ func newDocumentGetCmd(cli *CLI) *cobra.Command { Example: `$ vespa document get id:mynamespace:music::a-head-full-of-dreams...`, RunE: func(cmd *cobra.Command, args []string) error { waiter := cli.waiter(time.Duration(waitSecs)*time.Second, cmd) - return readDocuments(args, timeoutSecs, waiter, printCurl, cli, fieldSet, headers) + return readDocuments(args, timeoutSecs, waiter, printCurl, cli, fieldSet, headers, ignoreMissing) }, } cmd.Flags().StringVar(&fieldSet, "field-set", "", "Fields to include when reading document") + cmd.Flags().BoolVar(&ignoreMissing, "ignore-missing", false, "Ignore failure when getting non-existent documents") addDocumentFlags(cli, cmd, &printCurl, &timeoutSecs, &waitSecs, &headers) return cmd } @@ -309,7 +311,7 @@ func documentService(cli *CLI, waiter *Waiter) (*vespa.Service, error) { return waiter.Service(target, cli.config.cluster()) } -func printResult(cli *CLI, result OperationResult, payloadOnlyOnSuccess bool) error { +func printResult(cli *CLI, result OperationResult, payloadOnlyOnSuccess, ignoreFailure bool) error { out := cli.Stdout if !result.Success { out = cli.Stderr @@ -332,7 +334,7 @@ func printResult(cli *CLI, result OperationResult, payloadOnlyOnSuccess bool) er fmt.Fprintln(out, result.Payload) } - if !result.Success { + if !result.Success && !ignoreFailure { err := errHint(fmt.Errorf("document operation failed")) err.quiet = true return err diff --git a/client/go/internal/cli/cmd/document_test.go b/client/go/internal/cli/cmd/document_test.go index dce574a6cf2f..3b50b4a5eaa0 100644 --- a/client/go/internal/cli/cmd/document_test.go +++ b/client/go/internal/cli/cmd/document_test.go @@ -7,7 +7,7 @@ package cmd import ( "bytes" "encoding/json" - "fmt" + "errors" "io" "os" "strconv" @@ -131,6 +131,17 @@ func TestDocumentGet(t *testing.T) { []string{"id:mynamespace:music::a-head-full-of-dreams"}, t) } +func TestDocumentGetIgnoreMissing(t *testing.T) { + client := &mock.HTTPClient{} + client.NextResponseString(404, "{\"message\":\"not found\"}") + cli, stdout, stderr := newTestCLI(t) + cli.httpClient = client + assert.Nil(t, cli.Run("document", "get", "-t", "http://127.0.0.1:8080", "--ignore-missing", + "id:mynamespace:music::no-such-doc-1", "id:mynamespace:music::a-head-full-of-dreams")) + assert.Equal(t, "Success: Read id:mynamespace:music::a-head-full-of-dreams\n", stdout.String()) + assert.Equal(t, "Error: Invalid document operation: Status 404\n{\n \"message\": \"not found\"\n}\n", stderr.String()) +} + func TestDocumentGetWithHeader(t *testing.T) { client := &mock.HTTPClient{} assertDocumentGet(client, []string{"document", "get", "--header", "X-Foo: Bar", "id:mynamespace:music::a-head-full-of-dreams"}, @@ -224,7 +235,7 @@ func assertDocumentGet(client *mock.HTTPClient, args []string, documentIds []str func assertDocumentTransportError(t *testing.T, errorMessage string) { client := &mock.HTTPClient{} - client.NextResponseError(fmt.Errorf(errorMessage)) + client.NextResponseError(errors.New(errorMessage)) cli, _, stderr := newTestCLI(t) cli.httpClient = client assert.NotNil(t, cli.Run("-t", "http://127.0.0.1:8080", "document", "put",