Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Implement storager #5

Merged
merged 7 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export STORAGE_GDRIVE_NAME=demo
export STORAGE_GDRIVE_INTEGRATION_TEST=on
export STORAGE_GDRIVE_CREDENTIAL=apikey:apikey
11 changes: 11 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package gdrive

import (
"github.com/beyondstorage/go-storage/v4/services"
)

var (
ErrRateLimitExceeded = services.NewErrorCode("Rate limit exceeded")
junaire marked this conversation as resolved.
Show resolved Hide resolved
ErrBackEnd = services.NewErrorCode("Backend Error")
junaire marked this conversation as resolved.
Show resolved Hide resolved
ErrInvalidCredentials = services.NewErrorCode("Invalid credentials")
junaire marked this conversation as resolved.
Show resolved Hide resolved
)
30 changes: 30 additions & 0 deletions generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@ module github.com/beyondstorage/go-service-gdrive

go 1.15

require github.com/beyondstorage/go-storage/v4 v4.2.0
require (
github.com/beyondstorage/go-integration-test/v4 v4.1.1
github.com/beyondstorage/go-storage/v4 v4.2.0
github.com/google/uuid v1.2.0
google.golang.org/api v0.50.0
)
489 changes: 485 additions & 4 deletions go.sum

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions service.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name = "gdrive"

[namespace.storage]

[namespace.storage.new]
required = ["name","credential"]
optional = ["work_dir"]
[namespace.storage.op.create]
optional = ["object_mode"]

Expand Down
31 changes: 31 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## How run integration tests

### Run tests locally

Copy example files and update corresponding values.

```shell
cp Makefile.env.exmaple Makefile.env
```

Run tests

```shell
make integration_test
```

### Run tests in CI

Set following environment variables:

```shell
export STORAGE_GDRIVE_INTEGRATION_TEST=on
export STORAGE_GDRIVE_NAME=demo
export STORAGE_GDRIVE_CREDENTIAL=apikey:apikey
```

Run tests

```shell
make integration_test
```
15 changes: 15 additions & 0 deletions tests/storage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package tests

import (
"os"
"testing"

tests "github.com/beyondstorage/go-integration-test/v4"
)

func TestStorage(t *testing.T) {
if os.Getenv("STORAGE_GDRIVE_INTEGRATION_TEST") != "on" {
t.Skipf("STORAGE_GDRIVE_INTEGRATION_TEST is not 'on', skipped")
}
tests.TestStorager(t, setupTest(t))
}
33 changes: 33 additions & 0 deletions tests/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package tests

import (
"os"
"testing"

"github.com/beyondstorage/go-service-gdrive"
"github.com/beyondstorage/go-storage/v4/types"
ps "github.com/beyondstorage/go-storage/v4/pairs"

"github.com/google/uuid"
)

func setupTest(t *testing.T) types.Storager {
t.Log("Setup test for gdrive")

store, err := gdrive.NewStorager(
ps.WithName(os.Getenv("STORAGE_GDRIVE_NAME")),
ps.WithCredential(os.Getenv("STORAGE_GDRIVE_CREDENTIAL")),
ps.WithWorkDir("/"+uuid.New().String()),
)
if err != nil {
t.Errorf("new storager: %v", err)
}

t.Cleanup(func() {
err = store.Delete("")
if err != nil {
t.Errorf("cleanup: %v", err)
}
})
return store
}
100 changes: 97 additions & 3 deletions utils.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
package gdrive

import (
"context"
"fmt"

ps "github.com/beyondstorage/go-storage/v4/pairs"
"github.com/beyondstorage/go-storage/v4/pkg/credential"
"github.com/beyondstorage/go-storage/v4/services"
"github.com/beyondstorage/go-storage/v4/types"
"google.golang.org/api/drive/v3"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
)

// Storage is the example client.
type Storage struct {
name string
workDir string
service *drive.Service
//pairPolicy types.PairPolicy
junaire marked this conversation as resolved.
Show resolved Hide resolved
defaultPairs DefaultStoragePairs
features StorageFeatures

Expand All @@ -14,14 +27,95 @@ type Storage struct {

// String implements Storager.String
func (s *Storage) String() string {
panic("implement me")
return fmt.Sprintf(
"Storager gdrive {Name: %s, WorkDir: %s}",
s.name, s.workDir,
)
}

// NewStorager will create Storager only.
func NewStorager(pairs ...types.Pair) (types.Storager, error) {
panic("implement me")
return newStorager(pairs...)
}

func newStorager(pairs ...types.Pair) (store *Storage, err error) {
defer func() {
if err != nil {
err = services.InitError{Op: "new_storager", Type: Type, Err: formatError(err), Pairs: pairs}
}
}()

opt, err := parsePairStorageNew(pairs)
if err != nil {
return nil, err
}

store = &Storage{
name: opt.Name,
workDir: "/",
}
if opt.HasWorkDir {
store.workDir = opt.WorkDir
}
cred, err := credential.Parse(opt.Credential)
if err != nil {
return nil, err
}

//TODO: To make it easier, we just support authorized it
//via API key, maybe we can support OAuth in the future.
var token string
switch cred.Protocol() {
case credential.ProtocolAPIKey:
token = cred.APIKey()
default:
return nil, services.PairUnsupportedError{Pair: ps.WithCredential(opt.Credential)}
}

ctx := context.Background()
store.service, err = drive.NewService(ctx, option.WithAPIKey(token))
return store, nil
}

func formatError(err error) error {
if _, ok := err.(services.InternalError); ok {
return err
}

e, ok := err.(*googleapi.Error)
if !ok {
return fmt.Errorf("%w: %v", services.ErrUnexpected, err)
}

//According to the docs, errors with the same error code may have
//multiple causes, To determine the specific type of error,
//we should evaluate the reason filed of the returned JSON
//https://developers.google.com/drive/api/v3/handle-errors
switch e.Errors[0].Reason {
case "authError":
return fmt.Errorf("%w: %v", ErrInvalidCredentials, err)
case "dailyLimitExceeded", "rateLimitExceeded", "userRateLimitExceeded":
return fmt.Errorf("%w: %v", ErrRateLimitExceeded, err)
case "backendError":
return fmt.Errorf("%w: %v", ErrBackEnd, err)
case "notFound":
return fmt.Errorf("%w: %v", services.ErrObjectNotExist, err)
case "insufficientFilePermissions", "appNotAuthorizedToFile":
return fmt.Errorf("%w: %v", services.ErrPermissionDenied, err)
default:
return fmt.Errorf("%w: %v", services.ErrUnexpected, err)
}
}

func (s *Storage) formatError(op string, err error, path ...string) error {
panic("implement me")
if err == nil {
return nil
}

return services.StorageError{
Op: op,
Err: formatError(err),
Storager: s,
Path: path,
}
}