-
Notifications
You must be signed in to change notification settings - Fork 1
/
airtable.go
164 lines (138 loc) · 3.67 KB
/
airtable.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Package airtable provides a simple client for interacting
// with the Airtable API.
package airtable
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type Table interface {
List(opts Options) ([]Record, error)
Update(recs []Record) error
}
type GenericTable struct {
account *Account
Name string
View string
}
// getFullUrl builds the URL for making API call
func (t *GenericTable) getFullUrl() string {
return fmt.Sprintf("%s/v%s/%s/%s", t.account.BaseUrl, VERSION, t.account.BaseId, t.Name)
}
// NewTable returns a GenericTable struct that implements
// the Table interface
func NewTable(name string, account Account) Table {
if len(account.BaseUrl) == 0 {
account.BaseUrl = BASEURL
}
return &GenericTable{
account: &account,
Name: name,
View: VIEWNAME,
}
}
// List returns a list of records from the Airtable.
func (t *GenericTable) List(opts Options) ([]Record, error) {
allRecords := make([]Record, 0)
for {
// create req
req, err := http.NewRequest("GET", t.getFullUrl(), nil)
if err != nil {
return nil, fmt.Errorf("Failed to create request object")
}
// init query params
q := req.URL.Query()
// add maxrecords
switch opts.MaxRecords {
case 0:
q.Add("maxRecords", fmt.Sprint(MAXRECORDS))
default:
q.Add("maxRecords", fmt.Sprint(opts.MaxRecords))
}
// add offset
if len(opts.Offset) > 0 {
q.Add("offset", opts.Offset)
}
// add view
switch len(opts.View) {
case 0:
q.Add("view", fmt.Sprint(VIEWNAME))
default:
q.Add("view", fmt.Sprint(opts.View))
}
// add filters if they exist
if len(opts.Filter) > 0 {
q.Add("filterByFormula", opts.Filter)
}
// add sorting if provided
// sorting must be assembled as follows:
// [{"field": "my-field-name", "direction": "asc|desc"}]
// this is converted to:
// sort[0][field]=my-field-name
// sort[0][direction]=asc|desc
if len(opts.Sort) > 0 {
for i, sort := range opts.Sort {
for key, val := range sort {
q.Add(fmt.Sprintf("sort[%d][%s]", i, key), val)
}
}
}
// encode everything
req.URL.RawQuery = q.Encode()
// set headsers
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", t.account.ApiKey))
// make request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return []Record{}, fmt.Errorf("Error occured: %v", err)
}
defer resp.Body.Close()
// read response
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []Record{}, fmt.Errorf("Failed to read body: %v", err)
}
// unmarshal
ret := records{}
if err = json.Unmarshal(body, &ret); err != nil {
return []Record{}, fmt.Errorf("Failed to unmarshal response: %v", err)
}
allRecords = append(allRecords, ret.Records...)
// if an offset is returned then we have to redo the process
if ret.Offset == "" {
break
}
opts.Offset = ret.Offset
}
return allRecords, nil
}
// Update makes a PATCH request to all records provided to Airtable.
func (t *GenericTable) Update(recs []Record) error {
// assemble body
recordWrapper := records{
Records: recs,
}
jsonStr, err := json.Marshal(recordWrapper)
if err != nil {
return fmt.Errorf("Failed to create request body %v", err)
}
// create req
req, err := http.NewRequest("PATCH", t.getFullUrl(), bytes.NewBuffer(jsonStr))
if err != nil {
return fmt.Errorf("Failed to create new request %v", err)
}
// set headsers
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", t.account.ApiKey))
req.Header.Set("Content-Type", "application/json")
// make request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("Failed to make request %v", err)
}
defer resp.Body.Close()
return nil
}