...

Source file src/github.com/go-kivik/kivik/v4/couchdb/attachments.go

Documentation: github.com/go-kivik/kivik/v4/couchdb

     1  // Licensed under the Apache License, Version 2.0 (the "License"); you may not
     2  // use this file except in compliance with the License. You may obtain a copy of
     3  // the License at
     4  //
     5  //  http://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     9  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    10  // License for the specific language governing permissions and limitations under
    11  // the License.
    12  
    13  package couchdb
    14  
    15  import (
    16  	"context"
    17  	"errors"
    18  	"net/http"
    19  
    20  	"github.com/go-kivik/kivik/v4/couchdb/chttp"
    21  	"github.com/go-kivik/kivik/v4/driver"
    22  	internal "github.com/go-kivik/kivik/v4/int/errors"
    23  )
    24  
    25  func (d *db) PutAttachment(ctx context.Context, docID string, att *driver.Attachment, options driver.Options) (newRev string, err error) {
    26  	if docID == "" {
    27  		return "", missingArg("docID")
    28  	}
    29  	if att == nil {
    30  		return "", missingArg("att")
    31  	}
    32  	if att.Filename == "" {
    33  		return "", missingArg("att.Filename")
    34  	}
    35  	if att.Content == nil {
    36  		return "", missingArg("att.Content")
    37  	}
    38  
    39  	chttpOpts := chttp.NewOptions(options)
    40  
    41  	opts := map[string]interface{}{}
    42  	options.Apply(opts)
    43  	query, err := optionsToParams(opts)
    44  	if err != nil {
    45  		return "", err
    46  	}
    47  	var response struct {
    48  		Rev string `json:"rev"`
    49  	}
    50  	chttpOpts.Body = att.Content
    51  	chttpOpts.ContentType = att.ContentType
    52  	chttpOpts.Query = query
    53  	err = d.Client.DoJSON(ctx, http.MethodPut, d.path(chttp.EncodeDocID(docID)+"/"+att.Filename), chttpOpts, &response)
    54  	if err != nil {
    55  		return "", err
    56  	}
    57  	return response.Rev, nil
    58  }
    59  
    60  func (d *db) GetAttachmentMeta(ctx context.Context, docID, filename string, options driver.Options) (*driver.Attachment, error) {
    61  	resp, err := d.fetchAttachment(ctx, http.MethodHead, docID, filename, options)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	att, err := decodeAttachment(resp)
    66  	return att, err
    67  }
    68  
    69  func (d *db) GetAttachment(ctx context.Context, docID, filename string, options driver.Options) (*driver.Attachment, error) {
    70  	resp, err := d.fetchAttachment(ctx, http.MethodGet, docID, filename, options)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return decodeAttachment(resp)
    75  }
    76  
    77  func (d *db) fetchAttachment(ctx context.Context, method, docID, filename string, options driver.Options) (*http.Response, error) {
    78  	if method == "" {
    79  		return nil, errors.New("method required")
    80  	}
    81  	if docID == "" {
    82  		return nil, missingArg("docID")
    83  	}
    84  	if filename == "" {
    85  		return nil, missingArg("filename")
    86  	}
    87  	chttpOpts := chttp.NewOptions(options)
    88  
    89  	opts := map[string]interface{}{}
    90  	options.Apply(opts)
    91  	var err error
    92  	chttpOpts.Query, err = optionsToParams(opts)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	resp, err := d.Client.DoReq(ctx, method, d.path(chttp.EncodeDocID(docID)+"/"+filename), chttpOpts)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	return resp, chttp.ResponseError(resp)
   101  }
   102  
   103  func decodeAttachment(resp *http.Response) (*driver.Attachment, error) {
   104  	cType, err := getContentType(resp)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	digest, err := getDigest(resp)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	return &driver.Attachment{
   114  		ContentType: cType,
   115  		Digest:      digest,
   116  		Size:        resp.ContentLength,
   117  		Content:     resp.Body,
   118  	}, nil
   119  }
   120  
   121  func getContentType(resp *http.Response) (string, error) {
   122  	ctype := resp.Header.Get("Content-Type")
   123  	if _, ok := resp.Header["Content-Type"]; !ok {
   124  		return "", &internal.Error{Status: http.StatusBadGateway, Err: errors.New("no Content-Type in response")}
   125  	}
   126  	return ctype, nil
   127  }
   128  
   129  func getDigest(resp *http.Response) (string, error) {
   130  	etag, ok := chttp.ETag(resp)
   131  	if !ok {
   132  		return "", &internal.Error{Status: http.StatusBadGateway, Err: errors.New("ETag header not found")}
   133  	}
   134  	return etag, nil
   135  }
   136  
   137  func (d *db) DeleteAttachment(ctx context.Context, docID, filename string, options driver.Options) (newRev string, err error) {
   138  	if docID == "" {
   139  		return "", missingArg("docID")
   140  	}
   141  	opts := map[string]interface{}{}
   142  	options.Apply(opts)
   143  	if rev, _ := opts["rev"].(string); rev == "" {
   144  		return "", missingArg("rev")
   145  	}
   146  	if filename == "" {
   147  		return "", missingArg("filename")
   148  	}
   149  
   150  	chttpOpts := chttp.NewOptions(options)
   151  
   152  	chttpOpts.Query, err = optionsToParams(opts)
   153  	if err != nil {
   154  		return "", err
   155  	}
   156  	var response struct {
   157  		Rev string `json:"rev"`
   158  	}
   159  
   160  	err = d.Client.DoJSON(ctx, http.MethodDelete, d.path(chttp.EncodeDocID(docID)+"/"+filename), chttpOpts, &response)
   161  	if err != nil {
   162  		return "", err
   163  	}
   164  	return response.Rev, nil
   165  }
   166  

View as plain text