Showing
8 changed files
with
237 additions
and
5 deletions
api/.gitignore
0 → 100644
... | @@ -3,17 +3,19 @@ module classroom | ... | @@ -3,17 +3,19 @@ module classroom |
3 | go 1.14 | 3 | go 1.14 |
4 | 4 | ||
5 | require ( | 5 | require ( |
6 | + classroom/endpoints v0.0.0 | ||
7 | + classroom/functions v0.0.0 | ||
8 | + classroom/models v0.0.0 | ||
9 | + classroom/utils v0.0.0 | ||
6 | github.com/go-sql-driver/mysql v1.5.0 | 10 | github.com/go-sql-driver/mysql v1.5.0 |
7 | github.com/julienschmidt/httprouter v1.3.0 | 11 | github.com/julienschmidt/httprouter v1.3.0 |
8 | github.com/rs/cors v1.7.0 | 12 | github.com/rs/cors v1.7.0 |
9 | - | 13 | + google.golang.org/api v0.36.0 |
10 | - classroom/endpoints v0.0.0 | ||
11 | - classroom/models v0.0.0 | ||
12 | - classroom/functions v0.0.0 | ||
13 | ) | 14 | ) |
14 | 15 | ||
15 | replace ( | 16 | replace ( |
16 | classroom/endpoints v0.0.0 => ./endpoints | 17 | classroom/endpoints v0.0.0 => ./endpoints |
17 | - classroom/models v0.0.0 => ./models | ||
18 | classroom/functions v0.0.0 => ./functions | 18 | classroom/functions v0.0.0 => ./functions |
19 | + classroom/models v0.0.0 => ./models | ||
20 | + classroom/utils v0.0.0 => ./utils | ||
19 | ) | 21 | ) | ... | ... |
This diff is collapsed. Click to expand it.
api/utils/go.mod
0 → 100644
api/utils/go.sum
0 → 100644
This diff is collapsed. Click to expand it.
api/utils/google.go
0 → 100644
1 | +package utils | ||
2 | + | ||
3 | +import ( | ||
4 | + "encoding/json" | ||
5 | + "fmt" | ||
6 | + "log" | ||
7 | + "net/http" | ||
8 | + "os" | ||
9 | + | ||
10 | + "golang.org/x/net/context" | ||
11 | + "golang.org/x/oauth2" | ||
12 | +) | ||
13 | + | ||
14 | +// Retrieve a token, saves the token, then returns the generated client. | ||
15 | +func getClient(config *oauth2.Config) *http.Client { | ||
16 | + // The file token.json stores the user's access and refresh tokens, and is | ||
17 | + // created automatically when the authorization flow completes for the first | ||
18 | + // time. | ||
19 | + tokFile := "token.json" | ||
20 | + tok, err := tokenFromFile(tokFile) | ||
21 | + if err != nil { | ||
22 | + tok = getTokenFromWeb(config) | ||
23 | + saveToken(tokFile, tok) | ||
24 | + } | ||
25 | + return config.Client(context.Background(), tok) | ||
26 | +} | ||
27 | + | ||
28 | +// Request a token from the web, then returns the retrieved token. | ||
29 | +func getTokenFromWeb(config *oauth2.Config) *oauth2.Token { | ||
30 | + authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) | ||
31 | + fmt.Printf("Go to the following link in your browser then type the "+ | ||
32 | + "authorization code: \n%v\n", authURL) | ||
33 | + | ||
34 | + var authCode string | ||
35 | + if _, err := fmt.Scan(&authCode); err != nil { | ||
36 | + log.Fatalf("Unable to read authorization code: %v", err) | ||
37 | + } | ||
38 | + | ||
39 | + tok, err := config.Exchange(context.TODO(), authCode) | ||
40 | + if err != nil { | ||
41 | + log.Fatalf("Unable to retrieve token from web: %v", err) | ||
42 | + } | ||
43 | + return tok | ||
44 | +} | ||
45 | + | ||
46 | +// Retrieves a token from a local file. | ||
47 | +func tokenFromFile(file string) (*oauth2.Token, error) { | ||
48 | + f, err := os.Open(file) | ||
49 | + if err != nil { | ||
50 | + return nil, err | ||
51 | + } | ||
52 | + defer f.Close() | ||
53 | + tok := &oauth2.Token{} | ||
54 | + err = json.NewDecoder(f).Decode(tok) | ||
55 | + return tok, err | ||
56 | +} | ||
57 | + | ||
58 | +// Saves a token to a file path. | ||
59 | +func saveToken(path string, token *oauth2.Token) { | ||
60 | + fmt.Printf("Saving credential file to: %s\n", path) | ||
61 | + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) | ||
62 | + if err != nil { | ||
63 | + log.Fatalf("Unable to cache oauth token: %v", err) | ||
64 | + } | ||
65 | + defer f.Close() | ||
66 | + json.NewEncoder(f).Encode(token) | ||
67 | +} |
api/utils/notation.go
0 → 100644
api/utils/sheets.go
0 → 100644
1 | +package utils | ||
2 | + | ||
3 | +import ( | ||
4 | + "context" | ||
5 | + "fmt" | ||
6 | + "io/ioutil" | ||
7 | + "log" | ||
8 | + | ||
9 | + "golang.org/x/oauth2/google" | ||
10 | + "google.golang.org/api/option" | ||
11 | + "google.golang.org/api/sheets/v4" | ||
12 | +) | ||
13 | + | ||
14 | +type SheetsService struct { | ||
15 | + srv *sheets.Service | ||
16 | + ctx context.Context | ||
17 | +} | ||
18 | + | ||
19 | +func NewSheetsService(credentialsPath string) (*SheetsService, error) { | ||
20 | + b, err := ioutil.ReadFile(credentialsPath) | ||
21 | + if err != nil { | ||
22 | + log.Fatalf("Unable to read client secret file: %v", err) | ||
23 | + } | ||
24 | + | ||
25 | + // If modifying these scopes, delete your previously saved token.json. | ||
26 | + config, err := google.ConfigFromJSON(b, "https://www.googleapis.com/auth/spreadsheets") | ||
27 | + if err != nil { | ||
28 | + log.Fatalf("Unable to parse client secret file to config: %v", err) | ||
29 | + } | ||
30 | + client := getClient(config) | ||
31 | + | ||
32 | + ctx := context.Background() | ||
33 | + sheetsService, err := sheets.NewService(ctx, | ||
34 | + option.WithHTTPClient(client), | ||
35 | + option.WithScopes(sheets.SpreadsheetsScope), | ||
36 | + ) | ||
37 | + if err != nil { | ||
38 | + return nil, err | ||
39 | + } | ||
40 | + | ||
41 | + srv := &SheetsService{ | ||
42 | + srv: sheetsService, | ||
43 | + ctx: ctx, | ||
44 | + } | ||
45 | + | ||
46 | + return srv, nil | ||
47 | +} | ||
48 | + | ||
49 | +func (s *SheetsService) WriteAndMerge(sr SheetsRequest) error { | ||
50 | + req := &sheets.Request{} | ||
51 | + req.MergeCells = &sheets.MergeCellsRequest{ | ||
52 | + MergeType: "MERGE_ALL", | ||
53 | + Range: sr.Range, | ||
54 | + } | ||
55 | + | ||
56 | + rb := &sheets.BatchUpdateSpreadsheetRequest{ | ||
57 | + Requests: []*sheets.Request{req}, | ||
58 | + } | ||
59 | + | ||
60 | + _, err := s.srv.Spreadsheets.BatchUpdate(sr.SpreadSheetID, rb).Context(s.ctx).Do() | ||
61 | + if err != nil { | ||
62 | + return err | ||
63 | + } | ||
64 | + | ||
65 | + var vr sheets.ValueRange | ||
66 | + val := []interface{}{sr.Value} | ||
67 | + vr.Values = append(vr.Values, val) | ||
68 | + _, err = s.srv.Spreadsheets.Values.Append(sr.SpreadSheetID, sr.RangeStr, &vr).ValueInputOption("RAW").Do() | ||
69 | + if err != nil { | ||
70 | + return err | ||
71 | + } | ||
72 | + | ||
73 | + return nil | ||
74 | +} | ||
75 | + | ||
76 | +func (s *SheetsService) RemoveValue(sr SheetsRequest) error { | ||
77 | + req := &sheets.Request{} | ||
78 | + req.UnmergeCells = &sheets.UnmergeCellsRequest{ | ||
79 | + Range: sr.Range, | ||
80 | + } | ||
81 | + | ||
82 | + req2 := &sheets.Request{} | ||
83 | + req2.UpdateBorders = &sheets.UpdateBordersRequest{ | ||
84 | + Range: sr.Range, | ||
85 | + InnerHorizontal: &sheets.Border{ | ||
86 | + Color: &sheets.Color{ | ||
87 | + Blue: 0.0, | ||
88 | + Green: 0.0, | ||
89 | + Red: 0.0, | ||
90 | + }, | ||
91 | + Style: "SOLID", | ||
92 | + }, | ||
93 | + } | ||
94 | + | ||
95 | + rb := &sheets.BatchUpdateSpreadsheetRequest{ | ||
96 | + Requests: []*sheets.Request{req, req2}, | ||
97 | + } | ||
98 | + | ||
99 | + _, err := s.srv.Spreadsheets.BatchUpdate(sr.SpreadSheetID, rb).Context(s.ctx).Do() | ||
100 | + if err != nil { | ||
101 | + return err | ||
102 | + } | ||
103 | + | ||
104 | + _, err = s.srv.Spreadsheets.Values.Clear(sr.SpreadSheetID, sr.RangeStr, &sheets.ClearValuesRequest{}).Do() | ||
105 | + if err != nil { | ||
106 | + return err | ||
107 | + } | ||
108 | + | ||
109 | + return nil | ||
110 | +} | ||
111 | + | ||
112 | +type SheetsRequest struct { | ||
113 | + SpreadSheetID string | ||
114 | + SheetID int64 | ||
115 | + Range *sheets.GridRange | ||
116 | + RangeStr string | ||
117 | + Value string | ||
118 | +} | ||
119 | + | ||
120 | +func NewSheetsRequest( | ||
121 | + spreadSheetID string, sheetID int64, column string, start, end int64, value string) SheetsRequest { | ||
122 | + colIndex := A1ToInt(column) | ||
123 | + | ||
124 | + req := SheetsRequest{} | ||
125 | + req.SpreadSheetID = spreadSheetID | ||
126 | + req.SheetID = sheetID | ||
127 | + req.Range = &sheets.GridRange{ | ||
128 | + SheetId: sheetID, | ||
129 | + StartColumnIndex: colIndex, | ||
130 | + EndColumnIndex: colIndex + 1, | ||
131 | + StartRowIndex: start - 1, | ||
132 | + EndRowIndex: end, | ||
133 | + } | ||
134 | + req.RangeStr = fmt.Sprintf("%s%d:%s%d", column, start, column, end) | ||
135 | + req.Value = value | ||
136 | + return req | ||
137 | +} |
-
Please register or login to post a comment