github.com/wtfutil/wtf@v0.43.0/modules/gspreadsheets/client.go (about) 1 /* 2 * This butt-ugly code is direct from Google itself 3 * https://developers.google.com/sheets/api/quickstart/go 4 */ 5 6 package gspreadsheets 7 8 import ( 9 "context" 10 "encoding/json" 11 "fmt" 12 "log" 13 "net/http" 14 "net/url" 15 "os" 16 "os/user" 17 "path/filepath" 18 19 "github.com/wtfutil/wtf/utils" 20 "golang.org/x/oauth2" 21 "golang.org/x/oauth2/google" 22 "google.golang.org/api/option" 23 sheets "google.golang.org/api/sheets/v4" 24 ) 25 26 /* -------------------- Exported Functions -------------------- */ 27 28 func (widget *Widget) Fetch() ([]*sheets.ValueRange, error) { 29 ctx := context.Background() 30 31 secretPath, _ := utils.ExpandHomeDir(widget.settings.secretFile) 32 33 b, err := os.ReadFile(filepath.Clean(secretPath)) 34 if err != nil { 35 log.Fatalf("Unable to read secretPath. %v", err) 36 return nil, err 37 } 38 39 config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets.readonly") 40 if err != nil { 41 return nil, err 42 } 43 44 client := getClient(ctx, config) 45 46 srv, err := sheets.NewService(context.Background(), option.WithHTTPClient(client)) 47 if err != nil { 48 return nil, err 49 } 50 51 cells := utils.ToStrs(widget.settings.cellAddresses) 52 53 responses := make([]*sheets.ValueRange, len(cells)) 54 55 for i := 0; i < len(cells); i++ { 56 resp, getErr := srv.Spreadsheets.Values.Get(widget.settings.sheetID, cells[i]).Do() 57 if getErr != nil { 58 return nil, getErr 59 } 60 responses[i] = resp 61 } 62 63 return responses, err 64 } 65 66 /* -------------------- Unexported Functions -------------------- */ 67 68 // getClient uses a Context and Config to retrieve a Token 69 // then generate a Client. It returns the generated Client. 70 func getClient(ctx context.Context, config *oauth2.Config) *http.Client { 71 cacheFile, err := tokenCacheFile() 72 if err != nil { 73 log.Fatalf("Unable to get path to cached credential file. %v", err) 74 } 75 tok, err := tokenFromFile(cacheFile) 76 if err != nil { 77 tok = getTokenFromWeb(config) 78 saveToken(cacheFile, tok) 79 } 80 return config.Client(ctx, tok) 81 } 82 83 // getTokenFromWeb uses Config to request a Token. 84 // It returns the retrieved Token. 85 func getTokenFromWeb(config *oauth2.Config) *oauth2.Token { 86 authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) 87 fmt.Printf("Go to the following link in your browser then type the "+ 88 "authorization code: \n%v\n", authURL) 89 90 var code string 91 if _, err := fmt.Scan(&code); err != nil { 92 log.Fatalf("Unable to read authorization code %v", err) 93 } 94 95 tok, err := config.Exchange(context.Background(), code) 96 if err != nil { 97 log.Fatalf("Unable to retrieve token from web %v", err) 98 } 99 return tok 100 } 101 102 // tokenCacheFile generates credential file path/filename. 103 // It returns the generated credential path/filename. 104 func tokenCacheFile() (string, error) { 105 usr, err := user.Current() 106 if err != nil { 107 return "", err 108 } 109 110 tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials") 111 err = os.MkdirAll(tokenCacheDir, 0700) 112 if err != nil { 113 return "", err 114 } 115 116 return filepath.Join(tokenCacheDir, url.QueryEscape("spreadsheets-go-quickstart.json")), err 117 } 118 119 // tokenFromFile retrieves a Token from a given file path. 120 // It returns the retrieved Token and any read error encountered. 121 func tokenFromFile(file string) (*oauth2.Token, error) { 122 f, err := os.Open(filepath.Clean(file)) 123 if err != nil { 124 return nil, err 125 } 126 t := &oauth2.Token{} 127 err = json.NewDecoder(f).Decode(t) 128 defer func() { _ = f.Close() }() 129 return t, err 130 } 131 132 // saveToken uses a file path to create a file and store the 133 // token in it. 134 func saveToken(file string, token *oauth2.Token) { 135 fmt.Printf("Saving credential file to: %s\n", file) 136 f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 137 if err != nil { 138 log.Fatalf("Unable to cache oauth token: %v", err) 139 } 140 defer func() { _ = f.Close() }() 141 142 err = json.NewEncoder(f).Encode(token) 143 if err != nil { 144 log.Fatalf("Unable to encode oauth token: %v", err) 145 } 146 }