Skip to content

Commit

Permalink
Merge pull request #899 from go-kivik/getAttachment
Browse files Browse the repository at this point in the history
Get attachment
  • Loading branch information
flimzy authored Mar 4, 2024
2 parents 6d26097 + 9a67292 commit 8c2c4aa
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 4 deletions.
4 changes: 0 additions & 4 deletions x/sqlite/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ func (db) Changes(context.Context, driver.Options) (driver.Changes, error) {
return nil, nil
}

func (db) GetAttachment(context.Context, string, string, driver.Options) (*driver.Attachment, error) {
return nil, nil
}

func (db) Query(context.Context, string, string, driver.Options) (driver.Rows, error) {
return nil, nil
}
Expand Down
46 changes: 46 additions & 0 deletions x/sqlite/getattachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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 sqlite

import (
"context"
"database/sql"
"errors"
"fmt"
"net/http"

"github.com/go-kivik/kivik/v4/driver"
"github.com/go-kivik/kivik/v4/internal"
)

func (d *db) GetAttachment(ctx context.Context, docID string, filename string, _ driver.Options) (*driver.Attachment, error) {
attachment, err := d.attachmentExists(ctx, docID, filename)
if errors.Is(err, sql.ErrNoRows) {
return nil, &internal.Error{Status: http.StatusNotFound, Message: "Not Found: missing"}
}
if err != nil {
return nil, err
}

return attachment, nil
}

func (d *db) attachmentExists(ctx context.Context, docID string, filename string) (*driver.Attachment, error) {
var att driver.Attachment
err := d.db.QueryRowContext(ctx, fmt.Sprintf(`
SELECT filename, content_type, length, rev
FROM %s
WHERE id = $1 AND filename = $2
`, d.name+"_attachments"), docID, filename).Scan(&att.Filename, &att.ContentType, &att.Size, &att.RevPos)
return &att, err
}
150 changes: 150 additions & 0 deletions x/sqlite/getattachment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// 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.

//go:build !js
// +build !js

package sqlite

import (
"context"
"net/http"
"testing"

"github.com/google/go-cmp/cmp"
"gitlab.com/flimzy/testy"

"github.com/go-kivik/kivik/v4"
"github.com/go-kivik/kivik/v4/driver"
"github.com/go-kivik/kivik/v4/internal/mock"
)

func TestDBGetAttachment(t *testing.T) {
t.Parallel()
type attachmentMetadata struct {
Filename string
ContentType string
Length int64
RevPos int64
}
type test struct {
setup func(t *testing.T, db driver.DB)
docID string
filename string

wantAttachment *attachmentMetadata
wantStatus int
wantErr string
}

tests := testy.NewTable()
tests.Add("document does not exist", test{
docID: "foo",
filename: "foo.txt",
wantStatus: http.StatusNotFound,
wantErr: "Not Found: missing",
})
tests.Add("when the attachment exists, return it", test{
setup: func(t *testing.T, db driver.DB) {
_, err := db.Put(context.Background(), "foo", map[string]interface{}{
"_id": "foo",
"_attachments": map[string]interface{}{
"foo.txt": map[string]interface{}{
"content_type": "text/plain",
"data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==",
},
},
}, mock.NilOption)
if err != nil {
t.Fatal(err)
}
},
docID: "foo",
filename: "foo.txt",
})
tests.Add("when an attachment is returned, it contains metadata...", test{
setup: func(t *testing.T, db driver.DB) {
_, err := db.Put(context.Background(), "foo", map[string]interface{}{
"_id": "foo",
"_attachments": map[string]interface{}{
"foo.txt": map[string]interface{}{
"content_type": "text/plain",
"data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGluZw==",
},
},
}, mock.NilOption)
if err != nil {
t.Fatal(err)
}
},
docID: "foo",
filename: "foo.txt",
wantAttachment: &attachmentMetadata{
Filename: "foo.txt",
ContentType: "text/plain",
Length: 25,
RevPos: 1,
},
})
// GetAttachment returns the latest revision by default
//

/*
TODO:
- doc exists, and file exists, but doc is deleted
- return correct attachment in case of a conflict
- return existing file from existing doc
- request attachment from historical revision
- failure: request attachment from historical revision that does not exist
- GetAttachment returns 404 when the document does exist, but the attachment has never existed
- GetAttachment returns 404 when the document has never existed
- GetAttachment returns 404 when the document was deleted
- GetAttachment returns 404 when the latest revision was deleted
- GetAttachment returns 404 when the document does exist, but the attachment has been deleted
- GetAttachment returns the latest revision
*/

tests.Run(t, func(t *testing.T, tt test) {
t.Parallel()
db := newDB(t)
if tt.setup != nil {
tt.setup(t, db)
}
// opts := tt.options
// if opts == nil {
opts := mock.NilOption
// }
attachment, err := db.GetAttachment(context.Background(), tt.docID, tt.filename, opts)
if !testy.ErrorMatches(tt.wantErr, err) {
t.Errorf("Unexpected error: %s", err)
}
if status := kivik.HTTPStatus(err); status != tt.wantStatus {
t.Errorf("Unexpected status: %d", status)
}

if tt.wantAttachment == nil {
return
}
got := &attachmentMetadata{
Filename: attachment.Filename,
ContentType: attachment.ContentType,
Length: attachment.Size,
RevPos: attachment.RevPos,
}
if d := cmp.Diff(tt.wantAttachment, got); d != "" {
t.Errorf("Unexpected attachment metadata:\n%s", d)
}
})
}

0 comments on commit 8c2c4aa

Please sign in to comment.