Share Google Doc via Golang SDK - go

I am using service account JSON to create google Doc via Golang SDK but as this doc is only accessible to Service account I am not able to access it with my Personal Google Account. In the Google Doc SDK documentation I couldn't find any function to share the Doc.
This is my sample Code:
package googledoc
import (
"context"
"fmt"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2/google"
"google.golang.org/api/docs/v1"
"google.golang.org/api/option"
func CreateDoc(title string) error {
ctx := context.Background()
cred, err := GetSecrets("ap-south-1", "GOOGLE_SVC_ACC_JSON")
if err != nil {
return fmt.Errorf("unable to get the SSM %v", err)
}
config, err := google.JWTConfigFromJSON(cred, docs.DocumentsScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := config.Client(ctx)
srv, err := docs.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve Docs client: %v", err)
}
docObj := docs.Document{
Title: title,
}
doc, err := srv.Documents.Create(&docObj).Do()
if err != nil {
log.Fatalf("Unable to retrieve data from document: %v", err)
return err
}
fmt.Printf("The title of the doc is: %s %s\n", doc.Title, doc.DocumentId)
return nil
}
Any help with this would be really appreciated Thanks.

I believe your goal is as follows.
You want to share the created Google Document by the service account with your Google account.
You want to achieve this using googleapis for golang.
Unfortunately, Google Docs API cannot be used for sharing the Document with users. In this case, Drive API is used. When your script is modified using Drive API, how about the following modification?
Sample script:
Please add this script just after the line of fmt.Printf("The title of the doc is: %s %s\n", doc.Title, doc.DocumentId). By this, the created Google Document is shared with the user.
driveSrv, err := drive.NewService(ctx, option.WithHTTPClient(client)) // Please use your client and service for using Drive API.
if err != nil {
log.Fatal(err)
}
permission := &drive.Permission{
EmailAddress: "###", // Please set the email address you want to share.
Role: "writer",
Type: "user",
}
res, err := driveSrv.Permissions.Create(doc.DocumentId, permission).Do()
if err != nil {
log.Fatal(err)
return err
}
fmt.Println(res)
When this script is added to your script, the created Document is shared with the user as the writer.
Reference:
Permissions: create

Related

How to create a NamgedRange using google sheets API when doing a batch update with Go

I recently started writing a program to export CSV and SQL to google sheets. And in some scenarios I need to create a NamedRange while creating the sheet and/or updating it. The google official documentation is kinda confusion and not very helpful for me. Can anyone please show me an example code or point me in the right direction?
Right now I have something along these lines. This is just sample code to show one of the scenarios.
func writeSS(ssid string, content [][]interface{}) {
ctx := context.Background()
b, err := ioutil.ReadFile("./credentials/client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets.readonly")
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(config)
srv, err := sheets.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve Sheets client: %v", err)
}
spreadsheetId := ssid
rangeData := "Sheet name!A1:A6"
rb := &sheets.BatchUpdateValuesRequest{
ValueInputOption: "USER_ENTERED",
}
rb.Data = append(rb.Data, &sheets.ValueRange{
Range: rangeData,
Values: content,
})
_, err = srv.Spreadsheets.Values.BatchUpdate(spreadsheetId, rb).Context(ctx).Do() //Check this again
// _, err = srv.Spreadsheets.Values.Update(spreadsheetId, writeRange, &vr).ValueInputOption("USER_ENTERED").Do()
if err != nil {
log.Fatal(err)
}
fmt.Println("Done.")
}
I believe your goal is as follows.
You want to create a named range using googleapis with golang.
You have already been able to get and put values for Google Spreadsheet using Sheets API.
Modification points:
When I saw your script, the method of spreadsheets.values.batchUpdate of Sheets API is used. When you want to create the named range in the existing Google Spreadsheet, please use the method of spreadsheets.batchUpdate.
In your script, you are trying to put the values to the cells using the scope of https://www.googleapis.com/auth/spreadsheets.readonly. I think that an error related to the scopes occurs. In this case, please use the scope of https://www.googleapis.com/auth/spreadsheets.
When these points are reflected in your script, it becomes as follows.
Modified script:
config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets")
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(config)
srv, err := sheets.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
log.Fatalf("Unable to retrieve Sheets client: %v", err)
}
spreadsheetId := "###" // Please set your Spreadsheet ID.
sheetId := 1234567890 // Please set your sheet ID.
nameOfNamedRange := "sampleNamedRange1" // Please set the name of the named range.
req := sheets.Request{
AddNamedRange: &sheets.AddNamedRangeRequest{
NamedRange: &sheets.NamedRange{
Range: &sheets.GridRange{
SheetId: int64(sheetId),
StartRowIndex: 1,
EndRowIndex: 3,
StartColumnIndex: 1,
EndColumnIndex: 3,
},
Name: nameOfNamedRange,
},
},
}
requestBody := &sheets.BatchUpdateSpreadsheetRequest{
Requests: []*sheets.Request{&req},
}
resp, err := srv.Spreadsheets.BatchUpdate(spreadsheetId, requestBody).Do()
if err != nil {
log.Fatal(err)
}
fmt.Print(resp)
In this sample script, the gridrange of StartRowIndex: 1, EndRowIndex: 3, StartColumnIndex: 1, EndColumnIndex: 3, means the cells "B2:C3".
When this script is run, the named range of nameOfNamedRange is created with the range of "B2:C3" of sheetId in the Google Spreadsheet of spreadsheetId.
Note:
From your showing script, unfortunately, I cannot know the filename of the file including the access token and refresh token. If the filename is token.json used in the Quickstart, before you run the modified script, please delete the file. And, please reauthorize the scopes. Please be careful about this.
References:
Method: spreadsheets.batchUpdate
AddNamedRangeRequest

How to set the schedule to Google Calendar from google-api-go-client?

I wanna set the schedule to google calendar from google-api-go-client.
I got tired set the schedule with google calendar application :(
Is there any sample?
You can use quicksstart for Google Calendar API. The detail information is https://developers.google.com/google-apps/calendar/quickstart/go.
And when you want to create events, you can use "Events: insert". You can see the detail here.
It seems that you live in Japan. So when you use sample script, please be careful for DateTime and TimeZone.
If you use above both samples, main() becomes as follows. Before run sample script, please confirm whether Google Calendar API is enabled at Google API console. DateTime and TimeZone are for Japan. Please check above document sites about the detail.
Script :
func main() {
ctx := context.Background()
b, err := ioutil.ReadFile("client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/calendar-go-quickstart.json
config, err := google.ConfigFromJSON(b, calendar.CalendarScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(ctx, config)
srv, err := calendar.New(client)
if err != nil {
log.Fatalf("Unable to retrieve calendar Client %v", err)
}
event := &calendar.Event{
Summary: "Sample event",
Location: "Sample location",
Description: "This is a sample event.",
Start: &calendar.EventDateTime{
DateTime: "2017-04-22T00:00:00+09:00",
TimeZone: "Asia/Tokyo",
},
End: &calendar.EventDateTime{
DateTime: "2017-04-22T01:00:00+09:00",
TimeZone: "Asia/Tokyo",
},
}
calendarID := "#####"
event, err = srv.Events.Insert(calendarID, event).Do()
if err != nil {
log.Fatalf("Unable to create event. %v\n", err)
}
fmt.Printf("Event created: %s\n", event.HtmlLink)
}

Fetch Spreadsheet ID using Google Golang Sheets API V4 / Drive API V3?

I'm using Golang for a small project of mine and am currently trying to pull a spreadsheet ID given the exactly filesystem path (in Drive) and spreadsheet/worksheet name. However, looking through the API library in Golang, I don't see a function that allows me to do this.
I'm pretty new to this kind of programming in general so sorry in advance if this has a trivial solution.
Thanks!
You can use drive.files.list in Drive API at Google. drive.files.list can search files with folder information from your Google Drive.
From your question, I thought following 2 steps.
Search file using drive.files.list. File ID and parent folder id can be retrieved, simultaneously. In this case, the fields are id and parents.
Retrieve folder name from folder id using drive.files.get. The field is name.
You can make file tree using folder information got from each file.
About sample script, it used Go Quickstart for Drive API (https://developers.google.com/drive/v3/web/quickstart/go) Please change main() for a script of "Step 3: Set up the sample" to following script.
Script :
func main() {
ctx := context.Background()
b, err := ioutil.ReadFile("client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/drive-go-quickstart.json
config, err := google.ConfigFromJSON(b, drive.DriveMetadataReadonlyScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(ctx, config)
srv, err := drive.New(client)
if err != nil {
log.Fatalf("Unable to retrieve drive Client %v", err)
}
r, err := srv.Files.List().PageSize(10).
Fields("nextPageToken, files(id, name)").Do()
if err != nil {
log.Fatalf("Unable to retrieve files: %v", err)
}
// From here, it's sample script.
searchfile := "filename"
r, err := srv.Files.List().
Q("name=\"" + searchfile + "\" and trashed=false").Fields("files(id,parents)").Do() // "trashed=false" doesn't search in the trash box.
if err != nil {
log.Fatalf("Error: %v", err)
}
for _, i := range r.Files {
r, err := srv.Files.Get(i.Parents[0]).Fields("name").Do()
if err != nil {
log.Fatalf("Error: %v", err)
}
fmt.Printf("FileID=%s, FolderID=%s, FolderName=%s\n", i.Id, i.Parents[0], r.Name)
}
}
Result :
FileID=#####, FolderID=#####, FolderName=#####
Files on Google Drive can have several parent folders. At this script, it assumes that each file has one parent folder. If your files have several parent folders, please retrieve their folders from parent array.
References :
drive.files.list
https://developers.google.com/drive/v3/reference/files/list
drive.files.get
https://developers.google.com/drive/v3/reference/files/get
Go Quickstart for Drive API
https://developers.google.com/drive/v3/web/quickstart/go

Golang google sheets API V4 - Write/Update example?

Trying to write a simple three column table ([][]string) with Go, but can't.
The quick start guide is very nice, I now can read sheets, but there no any example of how to write data to a sheet, maybe it is trivial, but not for me it seems.
The Golang library for my brains is just too complicated to figure out.
And there not a single example I could google...
This C# example very looks close, but I am not sure I clearly understand C#
Well after some tryouts, there is an answer. Everything is same as in https://developers.google.com/sheets/quickstart/go Just changes in the main function
func write() {
ctx := context.Background()
b, err := ioutil.ReadFile("./Google_Sheets_API_Quickstart/client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-go-quickstart.json
config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets")
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
client := getClient(ctx, config)
srv, err := sheets.New(client)
if err != nil {
log.Fatalf("Unable to retrieve Sheets Client %v", err)
}
spreadsheetId := "YOUR SPREADSHEET ID"
writeRange := "A1"
var vr sheets.ValueRange
myval := []interface{}{"One", "Two", "Three"}
vr.Values = append(vr.Values, myval)
_, err = srv.Spreadsheets.Values.Update(spreadsheetId, writeRange, &vr).ValueInputOption("RAW").Do()
if err != nil {
log.Fatalf("Unable to retrieve data from sheet. %v", err)
}
}
Well if you are looking for Service Account based authentication, then the following worked for me.
Download the client secret file for service account from https://console.developers.google.com
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/api/option"
"google.golang.org/api/sheets/v4"
"log"
)
const (
client_secret_path = "./credentials/client_secret.json"
)
func NewSpreadsheetService() (*SpreadsheetService, error) {
// Service account based oauth2 two legged integration
ctx := context.Background()
srv, err := sheets.NewService(ctx, option.WithCredentialsFile(client_secret_path), option.WithScopes(sheets.SpreadsheetsScope))
if err != nil {
log.Fatalf("Unable to retrieve Sheets Client %v", err)
}
c := &SpreadsheetService{
service: srv,
}
return c, nil
}
func (s *SpreadsheetService) WriteToSpreadsheet(object *SpreadsheetPushRequest) error {
var vr sheets.ValueRange
vr.Values = append(vr.Values, object.Values)
res, err := s.service.Spreadsheets.Values.Append(object.SpreadsheetId, object.Range, &vr).ValueInputOption("RAW").Do()
fmt.Println("spreadsheet push ", res)
if err != nil {
fmt.Println("Unable to update data to sheet ", err)
}
return err
}
type SpreadsheetPushRequest struct {
SpreadsheetId string `json:"spreadsheet_id"`
Range string `json:"range"`
Values []interface{} `json:"values"`
}
change the scope in the quickstart example from spreadsheets.readonly to spreadsheets for r/w access
here is the write snippet:
writeRange := "A1" // or "sheet1:A1" if you have a different sheet
values := []interface{}{"It worked!"}
var vr sheets.ValueRange
vr.Values = append(vr.Values,values
_, err = srv.Spreadsheets.Values.Update(spreadsheetId,writeRange,&vr).ValueInputOption("RAW").Do()
and that should work:

Google Apps API 403 with Service Account

I've been trying make a query against Google's Admin API to list all users in my Google Apps Organization. I have permissions to make this query in the web UI example and get results, but it 403's when I try to make the query with a service account.
import (
"fmt"
"io/ioutil"
"log"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
directory "google.golang.org/api/admin/directory_v1"
)
func main() {
serviceAccountJSON, err := ioutil.ReadFile(serviceAccountFile)
if err != nil {
log.Fatalf("Could not read service account credentials file, %s => {%s}", serviceAccountFile, err)
}
config, err := google.JWTConfigFromJSON(serviceAccountJSON,
directory.AdminDirectoryUserScope,
directory.AdminDirectoryUserReadonlyScope,
)
client, err := directory.New(config.Client(context.Background()))
if err != nil {
log.Fatalf("Could not create directory service client => {%s}", err)
}
users, err := client.Users.List().ViewType(publicDataView).Domain(domain).Do()
if err != nil {
log.Fatalf("Failed to query all users => {%s}", err)
}
for _, u := range users.Users {
fmt.Println(u.Name.FullName)
}
}
Every time I execute I get a 403. The same query parameters works in the Try it! section here so I'm not sure why it fails.
result: Failed to query all users => {googleapi: Error 403: Not Authorized to access this resource/api, forbidden}
I know this question is a year old, but I couldnt find anything about this anywhere - but ive just managed to fix it after running into the same error as you.
basically you need to set a delegation user to your config, eg:
func main() {
serviceAccountJSON, err := ioutil.ReadFile(serviceAccountFile)
if err != nil {
log.Fatalf("Could not read service account credentials file, %s => {%s}", serviceAccountFile, err)
}
config, err := google.JWTConfigFromJSON(serviceAccountJSON,
directory.AdminDirectoryUserScope,
directory.AdminDirectoryUserReadonlyScope,
)
// Add me
config.Subject = "someone#example.com"
client, err := directory.New(config.Client(context.Background()))
if err != nil {
log.Fatalf("Could not create directory service client => {%s}", err)
}
users, err := client.Users.List().ViewType(publicDataView).Domain(domain).Do()
if err != nil {
log.Fatalf("Failed to query all users => {%s}", err)
}
for _, u := range users.Users {
fmt.Println(u.Name.FullName)
}
}
See https://github.com/golang/oauth2/blob/master/google/example_test.go#L118
hope this helps someone else!
The #Chronojam reply saved me hours, however on the newer versions of the Google SDK directory.New is deprecated, you should be achieve the same thing replacing the directory.New(...) line with this one instead:
client, err := directory.NewService(context.Background(), option.WithHTTPClient(config.Client(context.Background())))

Resources