-
Notifications
You must be signed in to change notification settings - Fork 508
Fix bug with missing downloadURL for docs/sheets #25
Changes from 3 commits
60e3583
565cc2e
bf65e21
5f66677
e366083
6e3ec61
2771355
0c4fac7
03ac716
1620196
478d24e
e447f83
2191a67
1e4095d
74d91bf
94ec557
aa77de6
7eaa27b
5bac0af
131bb86
28fd545
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,9 @@ type Options struct { | |
IsForce bool | ||
// Hidden discovers hidden paths if set | ||
Hidden bool | ||
// ExportOnBackup when set allows the exporting of Google Docs + Sheets to a | ||
// downloadable format e.g *.presentation to pptx. | ||
ExportOnBackup bool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Export bool There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Roger that. |
||
} | ||
|
||
type Commands struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ import ( | |
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
"path/filepath" | ||
"sync" | ||
) | ||
|
@@ -26,7 +27,7 @@ const ( | |
maxNumOfConcPullTasks = 4 | ||
) | ||
|
||
// Pull from remote if remote path exists and in a god context. If path is a | ||
// Pull from remote if remote path exists and in a gd context. If path is a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can drop gd here. This repo was once called god, and the comment is legacy now. god was much of an ambitious name for a Drive client :) |
||
// directory, it recursively pulls from the remote if there are remote changes. | ||
// It doesn't check if there are remote changes if isForce is set. | ||
func (g *Commands) Pull() (err error) { | ||
|
@@ -47,12 +48,12 @@ func (g *Commands) Pull() (err error) { | |
} | ||
|
||
if ok := printChangeList(cl, g.opts.IsNoPrompt); ok { | ||
return g.playPullChangeList(cl) | ||
return g.playPullChangeList(cl, g.opts.ExportOnBackup) | ||
} | ||
return | ||
} | ||
|
||
func (g *Commands) playPullChangeList(cl []*Change) (err error) { | ||
func (g *Commands) playPullChangeList(cl []*Change, exportOnBackup bool) (err error) { | ||
var next []*Change | ||
g.taskStart(len(cl)) | ||
|
||
|
@@ -72,9 +73,9 @@ func (g *Commands) playPullChangeList(cl []*Change) (err error) { | |
for _, c := range next { | ||
switch c.Op() { | ||
case OpMod: | ||
go g.localMod(&wg, c) | ||
go g.localMod(&wg, c, exportOnBackup) | ||
case OpAdd: | ||
go g.localAdd(&wg, c) | ||
go g.localAdd(&wg, c, exportOnBackup) | ||
case OpDelete: | ||
go g.localDelete(&wg, c) | ||
} | ||
|
@@ -86,20 +87,22 @@ func (g *Commands) playPullChangeList(cl []*Change) (err error) { | |
return err | ||
} | ||
|
||
func (g *Commands) localMod(wg *sync.WaitGroup, change *Change) (err error) { | ||
func (g *Commands) localMod(wg *sync.WaitGroup, change *Change, exportOnBackup bool) (err error) { | ||
defer g.taskDone() | ||
defer wg.Done() | ||
destAbsPath := g.context.AbsPathOf(change.Path) | ||
if change.Src.BlobAt != "" { | ||
|
||
if change.Src.BlobAt != "" || change.Src.ExportLinks != nil { | ||
// download and replace | ||
if err = g.download(change); err != nil { | ||
if err = g.download(change, exportOnBackup); err != nil { | ||
return | ||
} | ||
} | ||
return os.Chtimes(destAbsPath, change.Src.ModTime, change.Src.ModTime) | ||
} | ||
|
||
func (g *Commands) localAdd(wg *sync.WaitGroup, change *Change) (err error) { | ||
func (g *Commands) localAdd(wg *sync.WaitGroup, change *Change, exportOnBackup bool) (err error) { | ||
|
||
defer g.taskDone() | ||
defer wg.Done() | ||
destAbsPath := g.context.AbsPathOf(change.Path) | ||
|
@@ -108,9 +111,9 @@ func (g *Commands) localAdd(wg *sync.WaitGroup, change *Change) (err error) { | |
if change.Src.IsDir { | ||
return os.Mkdir(destAbsPath, os.ModeDir|0755) | ||
} | ||
if change.Src.BlobAt != "" { | ||
if change.Src.BlobAt != "" || change.Src.ExportLinks != nil { | ||
// download and create | ||
if err = g.download(change); err != nil { | ||
if err = g.download(change, exportOnBackup); err != nil { | ||
return | ||
} | ||
} | ||
|
@@ -123,8 +126,51 @@ func (g *Commands) localDelete(wg *sync.WaitGroup, change *Change) (err error) { | |
return os.RemoveAll(change.Dest.BlobAt) | ||
} | ||
|
||
func (g *Commands) download(change *Change) (err error) { | ||
destAbsPath := g.context.AbsPathOf(change.Path) | ||
func touchFile(path string) (err error) { | ||
var ef *os.File | ||
defer func() { | ||
if err != nil && ef != nil { | ||
ef.Close() | ||
} | ||
}() | ||
ef, err = os.Create(path) | ||
return | ||
} | ||
|
||
func (g *Commands) download(change *Change, exportOnBackup bool) (err error) { | ||
exportUrl := "" | ||
baseName := change.Path | ||
|
||
// If BlobAt is not set, we are most likely dealing with | ||
// Document/SpreadSheet/Image. In this case we'll use the target | ||
// exportable type since we cannot directly download the raw data. | ||
// We also need to pay attention and add the exported extension | ||
// to avoid overriding the original file on re-syncing. | ||
if len(change.Src.BlobAt) < 1 && exportOnBackup && IsGoogleDoc(change.Src) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this if be as simple as: if hasExportLinks(change.Str) && export { } Why do we have to care about BlobAt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good question, see I am thinking about the case that a file has both exportLinks as well as BlobAt. In this case we would want to download the actual file. |
||
var ok bool | ||
var mimeKeyExtList[]string | ||
|
||
mimeKeyExtList, ok = docExportsMap[change.Src.MimeType] | ||
if !ok { | ||
mimeKeyExtList = []string{"text/plain", "txt"} | ||
} | ||
|
||
// We need to touch an empty file for the | ||
// non-downloadable version to avoid an erasal | ||
// on later push. If there is a name conflict / data race, | ||
// the original file won't be touched. | ||
emptyFilepath := g.context.AbsPathOf(baseName) | ||
err = touchFile(emptyFilepath) | ||
|
||
// TODO: @odeke-em / @rakyll, if user selects all desired formats, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe export flag should accept a list of types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great suggestion, this solves a bunch of problems I was thinking of. |
||
// should we be be downloading every single one of them? | ||
exportUrl = change.Src.ExportLinks[mimeKeyExtList[0]] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. exportURL There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Roger that. |
||
fmt.Print("Exported ", baseName) | ||
baseName = strings.Join([]string{baseName, mimeKeyExtList[1]}, ".") | ||
fmt.Println(" to: ", baseName) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fmt.Printf("Exported %s to %s", exportURL, name) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, exportURL might not be descriptive to the user. |
||
} | ||
|
||
destAbsPath := g.context.AbsPathOf(baseName) | ||
var fo *os.File | ||
fo, err = os.Create(destAbsPath) | ||
if err != nil { | ||
|
@@ -144,7 +190,7 @@ func (g *Commands) download(change *Change) (err error) { | |
blob.Close() | ||
} | ||
}() | ||
blob, err = g.rem.Download(change.Src.Id) | ||
blob, err = g.rem.Download(change.Src.Id, exportUrl) | ||
if err != nil { | ||
return err | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,8 @@ var ( | |
ErrPathNotExists = errors.New("remote path doesn't exist") | ||
) | ||
|
||
var docExportsMap = *newDocExportsMap() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. var docExportTypes = map[string][]string {
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good plan. |
||
|
||
type Remote struct { | ||
transport *oauth.Transport | ||
service *drive.Service | ||
|
@@ -57,6 +59,18 @@ func NewRemoteContext(context *config.Context) *Remote { | |
return &Remote{service: service, transport: transport} | ||
} | ||
|
||
func IsGoogleDoc(f *File) bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't have to be in remote.go. It should be likely in pull.go. And it should have be imported. isGoogleDoc or better isExportable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aye aye, I actually initially had it in pull.go. However, remember that we don't wanna clobber Google Docs on the cloud with their empty replacements on disk. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, I now concur, I've encountered a case while coding up the solution :) |
||
if f == nil || f.IsDir { | ||
return false; | ||
} | ||
|
||
_, ok := docExportsMap[f.MimeType] | ||
if !ok { | ||
return f.BlobAt == ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. go fmt your code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :) |
||
} | ||
return true; | ||
} | ||
|
||
func RetrieveRefreshToken(context *config.Context) (string, error) { | ||
transport := newTransport(context) | ||
url := transport.Config.AuthCodeURL("") | ||
|
@@ -120,8 +134,14 @@ func (r *Remote) Publish(id string) (string, error) { | |
return "https://googledrive.com/host/" + id, nil | ||
} | ||
|
||
func (r *Remote) Download(id string) (io.ReadCloser, error) { | ||
resp, err := r.transport.Client().Get("https://googledrive.com/host/" + id) | ||
func (r *Remote) Download(id string, exportUrl string) (io.ReadCloser, error) { | ||
var url string | ||
if len(exportUrl) < 1 { | ||
url = "https://googledrive.com/host/" + id | ||
} else { | ||
url = exportUrl | ||
} | ||
resp, err := r.transport.Client().Get(url) | ||
if err != nil || resp.StatusCode < 200 || resp.StatusCode > 299 { | ||
return resp.Body, err | ||
} | ||
|
@@ -197,3 +217,19 @@ func newTransport(context *config.Context) *oauth.Transport { | |
}, | ||
} | ||
} | ||
|
||
func newDocExportsMap() *map[string][]string { | ||
return &map[string][]string { | ||
"text/plain": []string{"text/plain", "txt",}, | ||
"application/vnd.google-apps.drawing": []string{"image/svg+xml", "svg+xml",}, | ||
"application/vnd.google-apps.spreadsheet": []string{ | ||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "xlsx", | ||
}, | ||
"application/vnd.google-apps.document": []string{ | ||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "docx", | ||
}, | ||
"application/vnd.google-apps.presentation": []string{ | ||
"application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", | ||
}, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use
export
. Use less verbose variables in Go.(And I should cleanup the codebase to fix the legacy bad naming issues throughout the repo.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:)