Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 9 additions & 3 deletions block_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,23 @@ type Block struct {
type BlockUploadReq struct {
AddressID string
ShareID string
VolumeID string `json:",omitempty"`
LinkID string
RevisionID string

BlockList []BlockUploadInfo
}

type Verifier struct {
Token string
}

type BlockUploadInfo struct {
Index int
Size int64
EncSignature string
Hash string
Verifier *Verifier `json:",omitempty"`
Size int64 `json:",omitempty"`
EncSignature string `json:",omitempty"`
Hash string `json:",omitempty"`
}

type BlockUploadLink struct {
Expand Down
14 changes: 14 additions & 0 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ func (c *Client) MoveLink(ctx context.Context, shareID, linkID string, req MoveL
return nil
}

func (c *Client) MoveLinkByVolume(ctx context.Context, volumeID, linkID string, req MoveLinkReq) error {
var res struct {
Code int
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).SetBody(req).Put("/drive/v2/volumes/" + volumeID + "/links/" + linkID + "/move")
}); err != nil {
return err
}

return nil
}

func (c *Client) CreateFile(ctx context.Context, shareID string, req CreateFileReq) (CreateFileRes, error) {
var res struct {
Code int
Expand Down
20 changes: 20 additions & 0 deletions link_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,23 @@ func (c *Client) CreateRevision(ctx context.Context, shareID, linkID string) (Cr

return res.Revision, nil
}

func (c *Client) GetRevisionVerification(ctx context.Context, shareID, linkID, revisionID string) (RevisionVerificationRes, error) {
var res struct {
VerificationCode string
ContentKeyPacket string
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.
SetResult(&res).
Get("/drive/shares/" + shareID + "/links/" + linkID + "/revisions/" + revisionID + "/verification")
}); err != nil {
return RevisionVerificationRes{}, err
}

return RevisionVerificationRes{
VerificationCode: res.VerificationCode,
ContentKeyPacket: res.ContentKeyPacket,
}, nil
}
66 changes: 66 additions & 0 deletions link_file_route_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package proton_test

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

"github.com/rclone/go-proton-api"
"github.com/stretchr/testify/require"
)

func TestMoveLinkByVolumeUsesV2Endpoint(t *testing.T) {
var gotMethod string
var gotPath string

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotMethod = r.Method
gotPath = r.URL.Path
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"Code":1000}`))
}))
defer ts.Close()

m := proton.New(proton.WithHostURL(ts.URL))
defer m.Close()

c := m.NewClient("", "", "")
defer c.Close()

err := c.MoveLinkByVolume(context.Background(), "volume-id", "link-id", proton.MoveLinkReq{
ParentLinkID: "parent-link-id",
Name: "encrypted-name",
OriginalHash: "original-hash",
Hash: "new-hash",
})
require.NoError(t, err)
require.Equal(t, http.MethodPut, gotMethod)
require.Equal(t, "/drive/v2/volumes/volume-id/links/link-id/move", gotPath)
}

func TestGetRevisionVerificationUsesShareVerificationEndpoint(t *testing.T) {
var gotMethod string
var gotPath string

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotMethod = r.Method
gotPath = r.URL.Path
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"VerificationCode":"abc","ContentKeyPacket":"def"}`))
}))
defer ts.Close()

m := proton.New(proton.WithHostURL(ts.URL))
defer m.Close()

c := m.NewClient("", "", "")
defer c.Close()

res, err := c.GetRevisionVerification(context.Background(), "share-id", "link-id", "revision-id")
require.NoError(t, err)
require.Equal(t, http.MethodGet, gotMethod)
require.Equal(t, "/drive/shares/share-id/links/link-id/revisions/revision-id/verification", gotPath)
require.Equal(t, "abc", res.VerificationCode)
require.Equal(t, "def", res.ContentKeyPacket)
}
19 changes: 14 additions & 5 deletions link_file_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,17 @@ func GetNameHash(name string, hashKey []byte) (string, error) {
type MoveLinkReq struct {
ParentLinkID string

Name string // Encrypted File Name
OriginalHash string // Old Encrypted File Name Hash
Hash string // Encrypted File Name Hash by using parent's NodeHashKey
Name string // Encrypted File Name
NameSignatureEmail string `json:",omitempty"`
OriginalHash string // Old Encrypted File Name Hash
Hash string // Encrypted File Name Hash by using parent's NodeHashKey
ContentHash *string `json:",omitempty"`

NodePassphrase string // The passphrase used to unlock the NodeKey, encrypted by the owning Link/Share keyring.
NodePassphraseSignature string // The signature of the NodePassphrase
NodePassphraseSignature string `json:",omitempty"` // The signature of the NodePassphrase
SignatureEmail string `json:",omitempty"`

SignatureAddress string // Signature email address used to sign passphrase and name
SignatureAddress string `json:",omitempty"` // Signature email address used to sign passphrase and name
}

func (moveLinkReq *MoveLinkReq) SetName(name string, addrKR, nodeKR *crypto.KeyRing) error {
Expand Down Expand Up @@ -142,6 +146,11 @@ type CreateRevisionRes struct {
ID string // Encrypted Revision ID
}

type RevisionVerificationRes struct {
VerificationCode string
ContentKeyPacket string
}

type CommitRevisionReq struct {
ManifestSignature string
SignatureAddress string
Expand Down
6 changes: 5 additions & 1 deletion link_folder.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import (
"github.com/go-resty/resty/v2"
)

func isDeleteChildrenResponseCodeAllowed(code Code) bool {
return code == SuccessCode || code == AFileOrFolderNotFound
}

func (c *Client) ListChildren(ctx context.Context, shareID, linkID string, showAll bool) ([]Link, error) {
var res struct {
Links []Link
Expand Down Expand Up @@ -107,7 +111,7 @@ func (c *Client) DeleteChildren(ctx context.Context, shareID, linkID string, chi
}

for _, res := range res.Responses {
if res.Response.Code != SuccessCode {
if !isDeleteChildrenResponseCodeAllowed(res.Response.Code) {
return fmt.Errorf("failed to delete child: %w", res.Response)
}
}
Expand Down
24 changes: 24 additions & 0 deletions link_folder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package proton

import "testing"

func TestIsDeleteChildrenResponseCodeAllowed(t *testing.T) {
tests := []struct {
name string
code Code
want bool
}{
{name: "success", code: SuccessCode, want: true},
{name: "not_found", code: 2501, want: true},
{name: "conflict", code: AFileOrFolderNameExist, want: false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := isDeleteChildrenResponseCodeAllowed(tt.code)
if got != tt.want {
t.Fatalf("unexpected result: got=%v want=%v", got, tt.want)
}
})
}
}
45 changes: 25 additions & 20 deletions manager_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,32 @@ const (
)

type managerBuilder struct {
hostURL string
appVersion string
userAgent string
transport http.RoundTripper
verifyProofs bool
cookieJar http.CookieJar
retryCount int
logger resty.Logger
debug bool
panicHandler async.PanicHandler
hostURL string
appVersion string
driveSDKVersion string
userAgent string
transport http.RoundTripper
verifyProofs bool
cookieJar http.CookieJar
retryCount int
logger resty.Logger
debug bool
panicHandler async.PanicHandler
}

func newManagerBuilder() *managerBuilder {
return &managerBuilder{
hostURL: DefaultHostURL,
appVersion: DefaultAppVersion,
userAgent: DefaultUserAgent,
transport: http.DefaultTransport,
verifyProofs: true,
cookieJar: nil,
retryCount: 3,
logger: nil,
debug: false,
panicHandler: async.NoopPanicHandler{},
hostURL: DefaultHostURL,
appVersion: DefaultAppVersion,
driveSDKVersion: "",
userAgent: DefaultUserAgent,
transport: http.DefaultTransport,
verifyProofs: true,
cookieJar: nil,
retryCount: 3,
logger: nil,
debug: false,
panicHandler: async.NoopPanicHandler{},
}
}

Expand Down Expand Up @@ -80,6 +82,9 @@ func (builder *managerBuilder) build() *Manager {
// Set app version in header.
m.rc.OnBeforeRequest(func(_ *resty.Client, req *resty.Request) error {
req.SetHeader("x-pm-appversion", builder.appVersion)
if builder.driveSDKVersion != "" {
req.SetHeader("x-pm-drive-sdk-version", builder.driveSDKVersion)
}
req.SetHeader("User-Agent", builder.userAgent)
return nil
})
Expand Down
46 changes: 46 additions & 0 deletions manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,52 @@ import (
"github.com/stretchr/testify/require"
)

func TestManagerSetsSDKHeadersWhenConfigured(t *testing.T) {
var gotAppVersion string
var gotSDKVersion string
var gotUserAgent string

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAppVersion = r.Header.Get("x-pm-appversion")
gotSDKVersion = r.Header.Get("x-pm-drive-sdk-version")
gotUserAgent = r.Header.Get("User-Agent")
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()

m := proton.New(
proton.WithHostURL(ts.URL),
proton.WithAppVersion("web-drive@5.2.0+af66c8fa"),
proton.WithDriveSDKVersion("js@0.10.0"),
proton.WithUserAgent("go-proton-api-tests"),
)
defer m.Close()

require.NoError(t, m.Ping(context.Background()))
require.Equal(t, "web-drive@5.2.0+af66c8fa", gotAppVersion)
require.Equal(t, "js@0.10.0", gotSDKVersion)
require.Equal(t, "go-proton-api-tests", gotUserAgent)
}

func TestManagerOmitsSDKHeaderByDefault(t *testing.T) {
var gotSDKVersion string

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotSDKVersion = r.Header.Get("x-pm-drive-sdk-version")
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()

m := proton.New(
proton.WithHostURL(ts.URL),
proton.WithAppVersion("test-app-version"),
)
defer m.Close()

require.NoError(t, m.Ping(context.Background()))
require.Equal(t, "", gotSDKVersion)
}

func TestConnectionReuse(t *testing.T) {
s := server.New()
defer s.Close()
Expand Down
14 changes: 14 additions & 0 deletions option.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ func WithAppVersion(appVersion string) Option {
}
}

func WithDriveSDKVersion(driveSDKVersion string) Option {
return &withDriveSDKVersion{
driveSDKVersion: driveSDKVersion,
}
}

type withDriveSDKVersion struct {
driveSDKVersion string
}

func (opt withDriveSDKVersion) config(builder *managerBuilder) {
builder.driveSDKVersion = opt.driveSDKVersion
}

type withUserAgent struct {
userAgent string
}
Expand Down
1 change: 1 addition & 0 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const (
// ProtonDrive
AFileOrFolderNameExist Code = 2500
ADraftExist Code = 2500
AFileOrFolderNotFound Code = 2501
)

var (
Expand Down