github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/google/drive/service/service.go (about)

     1  /*
     2  Copyright 2013 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // DriveService translates blobserver.Storage methods
    18  // into Google Drive API methods.
    19  package service
    20  
    21  import (
    22  	"errors"
    23  	"fmt"
    24  	"io"
    25  	"math"
    26  	"net/http"
    27  
    28  	"camlistore.org/third_party/code.google.com/p/goauth2/oauth"
    29  	client "camlistore.org/third_party/code.google.com/p/google-api-go-client/drive/v2"
    30  )
    31  
    32  const (
    33  	MimeTypeDriveFolder = "application/vnd.google-apps.folder"
    34  	MimeTypeCamliBlob   = "application/vnd.camlistore.blob"
    35  )
    36  
    37  // DriveService wraps Google Drive API to implement utility methods to
    38  // be performed on the root Drive destination folder.
    39  type DriveService struct {
    40  	transport  *oauth.Transport
    41  	apiservice *client.Service
    42  	parentId   string
    43  }
    44  
    45  // New initiates a new DriveService.
    46  func New(transport *oauth.Transport, parentId string) (*DriveService, error) {
    47  	apiservice, err := client.New(transport.Client())
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	service := &DriveService{transport: transport, apiservice: apiservice, parentId: parentId}
    52  	return service, err
    53  }
    54  
    55  // Get retrieves a file with its title
    56  func (s *DriveService) Get(id string) (*client.File, error) {
    57  	req := s.apiservice.Files.List()
    58  	// TODO: use field selectors
    59  	query := fmt.Sprintf("'%s' in parents and title = '%s'", s.parentId, id)
    60  	req.Q(query)
    61  	files, err := req.Do()
    62  
    63  	if err != nil || len(files.Items) < 1 {
    64  		return nil, err
    65  	}
    66  	return files.Items[0], err
    67  }
    68  
    69  // Lists the folder identified by parentId.
    70  func (s *DriveService) List(pageToken string, limit int) (files []*client.File, next string, err error) {
    71  	req := s.apiservice.Files.List()
    72  	req.Q(fmt.Sprintf("'%s' in parents and mimeType != '%s'", s.parentId, MimeTypeDriveFolder))
    73  
    74  	if pageToken != "" {
    75  		req.PageToken(pageToken)
    76  	}
    77  
    78  	if limit > 0 {
    79  		req.MaxResults(int64(limit))
    80  	}
    81  
    82  	result, err := req.Do()
    83  	if err != nil {
    84  		return
    85  	}
    86  	return result.Items, result.NextPageToken, err
    87  }
    88  
    89  // Upsert inserts a file, or updates if such a file exists.
    90  func (s *DriveService) Upsert(id string, data io.Reader) (file *client.File, err error) {
    91  	if file, err = s.Get(id); err != nil {
    92  		return
    93  	}
    94  	if file == nil {
    95  		file = &client.File{Title: id}
    96  		file.Parents = []*client.ParentReference{
    97  			&client.ParentReference{Id: s.parentId},
    98  		}
    99  		file.MimeType = MimeTypeCamliBlob
   100  		return s.apiservice.Files.Insert(file).Media(data).Do()
   101  	}
   102  
   103  	// TODO: handle large blobs
   104  	return s.apiservice.Files.Update(file.Id, file).Media(data).Do()
   105  }
   106  
   107  // Fetch retrieves the metadata and contents of a file.
   108  func (s *DriveService) Fetch(id string) (body io.ReadCloser, size uint32, err error) {
   109  	file, err := s.Get(id)
   110  
   111  	// TODO: maybe in the case of no download link, remove the file.
   112  	// The file should have malformed or converted to a Docs file
   113  	// unwantedly.
   114  	if err != nil || file == nil || file.DownloadUrl != "" {
   115  		return
   116  	}
   117  
   118  	req, _ := http.NewRequest("GET", file.DownloadUrl, nil)
   119  	var resp *http.Response
   120  	if resp, err = s.transport.RoundTrip(req); err != nil {
   121  		return
   122  	}
   123  	if file.FileSize > math.MaxUint32 || file.FileSize < 0 {
   124  		err = errors.New("file too big")
   125  	}
   126  	return resp.Body, uint32(file.FileSize), err
   127  }
   128  
   129  // Stat retrieves file metadata and returns
   130  // file size. Returns error if file is not found.
   131  func (s *DriveService) Stat(id string) (int64, error) {
   132  	file, err := s.Get(id)
   133  	if err != nil || file == nil {
   134  		return 0, err
   135  	}
   136  	return file.FileSize, err
   137  }
   138  
   139  // Trash trashes an existing file.
   140  func (s *DriveService) Trash(id string) (err error) {
   141  	_, err = s.apiservice.Files.Trash(id).Do()
   142  	return
   143  }