How do I access custom fields in an error? - go

Objective
Add a command to dropbox's CLI tool to get the shared link for the given path (file or folder).
The changes are here: github fork.
Background
The dropbox-go-sdk has a function that takes a path, and returns a new shared link, or returns an error containing the existing shared link.
I don't know how to use the error to extract the existing shared link.
Code
on github, and snippet here:
dbx := sharing.New(config)
res, err := dbx.CreateSharedLinkWithSettings(arg)
if err != nil {
switch e := err.(type) {
case sharing.CreateSharedLinkWithSettingsAPIError:
fmt.Printf("%v", e.EndpointError)
default:
return err
}
}
This prints the following:
&{{shared_link_already_exists} <nil> <nil>}found unknown shared link typeError: shared_link_already_exists/...
tracing:
CreateSharedLinkWithSettings --> CreateSharedLinkWithSettingsAPIError --> CreateSharedLinkWithSettingsError --> SharedLinkAlreadyExistsMetadata --> IsSharedLinkMetadata
IsSharedLinkMetadata contains the Url that I'm looking for.
More Info
The API docs point to CreateSharedLinkWithSettings, which should pass back the information in the error including the existing Url.
I struggle to understand how to deal with the error and extract the url from it.
The dbxcli has some code doing a similar operation, but again, not sure how it's working enough to apply it to the code I'm working on. Is it a Struct? Map? I don't know what this thing is called. There's some weird magic err.(type) stuff happening in the code. How do I access the data?

dbx := sharing.New(config)
res, err := dbx.CreateSharedLinkWithSettings(arg)
if err != nil {
switch e := err.(type) {
case sharing.CreateSharedLinkWithSettingsAPIError:
fmt.Printf("%v", e.EndpointError)
// type cast to the specific error and access the field you want.
settingsError := err.(sharing.CreateSharedLinkWithSettingsAPIError)
fmt.Println(settingsError.EndpointError.SharedLinkAlreadyExists.Metadata.Url)
default:
return err
}
}

The question was answered in the comments by #jimb. The answer is you access the fields like any other golang data structure - nothing special.
The errors I got when trying to access the fields were because the fields were not there.
The problem with the code was dependency issues. The code depends on an older version of the go-sdk and I referenced the latest version.
This question serves as a good explanation for how real golang programmers handle errors in their code with examples. I wasn't able to find this online, so I won't close the question.

Related

How to get a list of all files in directory with Google Drive API(v3)

I have stuck with function that must to return me a list of all files from directory (in this case directory is "root"). When I call this function, it return me files that only I added with my program (this program also can upload files to Google Drive), not all files. And it also shows me files that I deleted :/. What I do wrong?
This function I was copied from Google Drive API Quickstart
service, err := getService()
if err != nil {
log.Fatalf("Unable to retrieve Drive client: %v", err)
}
r, err := service.Files.List().Q("'root' in parents").Do()
if err != nil {
log.Fatalf("Unable to retrieve files: %v", err)
}
fmt.Println("Files:")
if len(r.Files) == 0 {
fmt.Println("No files found.")
} else {
for _, i := range r.Files {
fmt.Printf("%v (%vs )\n", i.Name, i.Id)
}
}
You want to retrieve all files just under the root folder.
You want to achieve this using google-api-go-client with golang.
You have already been get and put values for Google Drive using Drive API.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Issue and workaround:
From the situation of When I call this function, it return me files that only I added with my program (this program also can upload files to Google Drive), not all files., I thought that your scopes might include https://www.googleapis.com/auth/drive.file. When https://www.googleapis.com/auth/drive.file is used as the scope, only the files created by the application are retrieved.
In order to retrieve all files just under the root folder, please use the following scopes.
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.metadata.readonly
https://www.googleapis.com/auth/drive.metadata.
If you want to retrieve only the file list, the scopes of .readonly can be used.
Modified script:
From your question, I could notice that you are using google-api-go-client with golang and Go Quickstart. In this case, how about the following modification?
If drive.DriveFileScope is included in the scopes, please modify as follows.
From:
config, err := google.ConfigFromJSON(b, drive.DriveFileScope)
To:
config, err := google.ConfigFromJSON(b, drive.DriveMetadataScope)
or
config, err := google.ConfigFromJSON(b, drive.DriveReadonlyScope)
If you want to also upload the file, please use drive.DriveScope.
Note:
When you modified the scopes, please remove the file of token.json of tokFile := "token.json". And please run the script and authorize again. By this, the modified scopes are reflected to the access token and refresh token. Please be careful this.
References:
google-api-go-client
Go Quickstart
Files: list
If I misunderstood your question and this was not the direction you want, I apologize.

How can I use dynamic errors while retaining comparability for testing?

In go I often use
func MyFunc(s someInterface) error {
err := OtherFunc(s)
return fmt.Errorf("something wrong: %s", err)
}
So I lose the original error value, because I just take the error string and forge it into a new error. That is what I mean with dynamic errors.
Now consider a test for MyFunc():
func TestMyFunc(t *testing.T) {
s := mockSomeInterface()
testErr := MyFunc(s)
if testErr != interfaceSpecificErrorValue {
t.Errorf("fail")
}
}
What would I use for interfaceSpecificErrorValue (which is specific to someInterface in this example)? Or how could I make this testable?
I understand that I can solve this by defining all my possible errors beforehand and give them a constant value. What I am interested in is if there is another way to achieve this, because I like the hierarchical error messages that you can dynamically build up using fmt.Errorf("...: %s, err). There must be a good way to keep the error hierarchy without losing the original value.
(Comparing the output of the Error() method is a possibility, but not a good one.)
My preliminary answer to this is: In Go currently there is no canonical way to achieve nested errors with comparable error values.
After reading the offical documents and blog posts on error handling again, quite some source code from standard libraries, and finally this proposal: https://github.com/golang/proposal/blob/master/design/go2draft-error-values-overview.md, I decided I will keep my error types simple and static and wait for Go 2 to offer a better way.

How to get all Modifying Users of a specific Revision

Follow up on Google Drive Rest API : How to get all Modifying Users of a specific Revision
It has been approximately 3 years, so I am not sure what the status is, but I thought I would ask again on the status.
I see that Google Drive API # https://developers.google.com/drive/api/v3/reference/revisions/get
should actually do exactly what has been asked here, but when I make a call to the API it returns null for LastModifyingUser
I am not sure if this is a work in progress API or I am doing something wrong, so any help would be appreciated.
Just to provide some reference, I am posting some basic code that is an addition to what can be found here... https://developers.google.com/drive/api/v3/quickstart/go
revision, err := srv.Revisions.Get(fileId, revisionId).Do() //fieldId and revisionId are fatched using proper calls
if err != nil {
log.Fatalf("Unable to retrieve revision: %v", err)
}
fmt.Println("Revision:")
fmt.Printf("%+v\n", revision.LastModifyingUser)
You want to retrieve the value of lastModifyingUser from Revisions.Get() using Drive API v3.
If my understanding is correct, how about adding the fields? At the default, the fields are id,mimeType,modifiedTime. So when you want to retrieve only values of lastModifyingUser, please modify as follows.
From:
revision, err := srv.Revisions.Get(fileId, revisionId).Do()
To:
revision, err := srv.Revisions.Get(fileID, revisionID).Fields("lastModifyingUser").Do()
Note:
In this modified script, it is supposes that when you run your current script, no error occurs.
If you want to add lastModifyingUser to the default values of id,mimeType,modifiedTime, please set the fields to id,mimeType,modifiedTime,lastModifyingUser.
Reference:
Revisions
If I misunderstand your question, I'm sorry.

Logged in user, Windows, in Golang

I need to get the currently logged in user(s) on the local Windows machine, using Golang. I'm not looking for the user currently running the application, which can be got from the built-in function user.Current().
I can call query user from cmd and this gives me the list (string manipulation required, but that is not a problem) of users I need.
The code I have tried is:
out, err := exec.Command("query", "user")
if err != nil {
panic(err)
}
// ...do something with 'out'
This produces the error panic: exit status 1. The same occurs if I do:
out, err := exec.Command("cmd", "/C", "query", "user")
...
As usually with such kind of questions,
the solution is to proceed like this:
Research (using MSDN and other sources) on how to achieve the
stated goal using Win32 API.
Use the built-in syscall package (or, if available/desirable,
helper 3rd-party packages) to make those calls from Go.
The first step can be this
which yields the solution
which basically is "use WTS".
The way to go is to
Connect to the WTS subsystem¹.
Enumerate the currently active sessions.
Query each one for the information about the
identity of the user associated with it.
The second step is trickier but basically you'd need to research
how others do that.
See this
and this
and this for a few examples.
You might also look at files named _windows*.go in
the Go source of the syscall package.
¹ Note that even on a single-user machine, everything
related to seat/session management comes through WTS
(however castrated it is depending on a particular flavor of Windows). This is true since at least XP/W2k3.
package main
import (
"fmt"
"os/exec"
)
func main() {
out, err := exec.Command("cmd", "/C", "query user").Output()
if err != nil {
fmt.Println("Error: ", err)
}
fmt.Println(string(out))
}

Use package file to write to Cloud Storage?

Golang provides the file package to access Cloud Storage.
The package's Create function requires the io.WriteCloser interface. However, I have not found a single sample or documentation showing how to actually save a file to Cloud Storage.
Can anybody help? Is there a higher level implementation of io.WriteCloser that would allow us to store files in Cloud Storage? Any sample code?
We've obviously tried to Google it ourselves but found nothing and now hope for the community to help.
It's perhaps true than the behavior is not well defined in the documentation.
If you check the code: https://code.google.com/p/appengine-go/source/browse/appengine/file/write.go#133
In each call to Write the data is sent to the cloud (line 139). So you don't need to save. (You should close the file when you're done, through.)
Anyway, I'm confused with your wording: "The package's Create function requires the io.WriteCloser interface." That's not true. The package's Create functions returns a io.WriteCloser, that is, a thingy you can write to and close.
yourFile, _, err := Create(ctx, "filename", nil)
// Check err != nil here.
defer func() {
err := yourFile.Close()
// Check err != nil here.
}()
yourFile.Write([]byte("This will be sent to the file immediately."))
fmt.Fprintln(yourFile, "This too.")
io.Copy(yourFile, someReader)
This is how interfaces work in Go. They just provide you with a set of methods you can call, hiding the actual implementation from you; and, when you just depend on a particular interface instead of a particular implementation, you can combine in multiple ways, as fmt.Fprintln and io.Copy do.

Resources