impr: add custom context and custom client posibility #15
This commit is contained in:
@@ -43,6 +43,14 @@ You should get `your_api_token` in the airtable [account page](https://airtable.
|
||||
client := airtable.NewClient("your_api_token")
|
||||
```
|
||||
|
||||
You can use custom http client here
|
||||
```Go
|
||||
client.SetCustomClient(http.DefaultClient)
|
||||
```
|
||||
|
||||
### Custom context
|
||||
Each method below can be used with custom context. Simply use `MethodNameContext` call and provide context as first argument.
|
||||
|
||||
### List bases
|
||||
|
||||
```Go
|
||||
|
||||
30
base.go
30
base.go
@@ -1,6 +1,7 @@
|
||||
package airtable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,7 @@ type Field struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type View struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
@@ -45,9 +47,15 @@ type Tables struct {
|
||||
// GetBasesWithParams get bases with url values params
|
||||
// https://airtable.com/developers/web/api/list-bases
|
||||
func (at *Client) GetBasesWithParams(params url.Values) (*Bases, error) {
|
||||
return at.GetBasesWithParamsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// getBasesWithParamsContext get bases with url values params
|
||||
// with custom context
|
||||
func (at *Client) GetBasesWithParamsContext(ctx context.Context, params url.Values) (*Bases, error) {
|
||||
bases := new(Bases)
|
||||
|
||||
err := at.get("meta", "bases", "", params, bases)
|
||||
err := at.get(ctx, "meta", "bases", "", params, bases)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,7 +67,6 @@ func (at *Client) GetBasesWithParams(params url.Values) (*Bases, error) {
|
||||
type BaseConfig struct {
|
||||
client *Client
|
||||
dbId string
|
||||
params url.Values
|
||||
}
|
||||
|
||||
// GetBase return Base object.
|
||||
@@ -72,15 +79,26 @@ func (c *Client) GetBaseSchema(dbId string) *BaseConfig {
|
||||
|
||||
// Do send the prepared
|
||||
func (b *BaseConfig) Do() (*Tables, error) {
|
||||
return b.GetTablesWithParams()
|
||||
return b.GetTables()
|
||||
}
|
||||
|
||||
// GetTablesWithParams get tables from a base with url values params
|
||||
// Do send the prepared with custom context
|
||||
func (b *BaseConfig) DoContext(ctx context.Context) (*Tables, error) {
|
||||
return b.GetTablesContext(ctx)
|
||||
}
|
||||
|
||||
// GetTables get tables from a base with url values params
|
||||
// https://airtable.com/developers/web/api/get-base-schema
|
||||
func (b *BaseConfig) GetTablesWithParams() (*Tables, error) {
|
||||
func (b *BaseConfig) GetTables() (*Tables, error) {
|
||||
return b.GetTablesContext(context.Background())
|
||||
}
|
||||
|
||||
// getTablesContext get tables from a base with url values params
|
||||
// with custom context
|
||||
func (b *BaseConfig) GetTablesContext(ctx context.Context) (*Tables, error) {
|
||||
tables := new(Tables)
|
||||
|
||||
err := b.client.get("meta/bases", b.dbId, "tables", nil, tables)
|
||||
err := b.client.get(ctx, "meta/bases", b.dbId, "tables", nil, tables)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetBaseSchema(t *testing.T) {
|
||||
func TestGetBases(t *testing.T) {
|
||||
client := testClient(t)
|
||||
baseschema := client.GetBaseSchema("test")
|
||||
baseschema.client.baseURL = mockResponse("base_schema.json").URL
|
||||
|
||||
29
client.go
29
client.go
@@ -11,7 +11,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
@@ -42,6 +42,11 @@ func NewClient(apiKey string) *Client {
|
||||
}
|
||||
}
|
||||
|
||||
// Set custom http client for custom usage
|
||||
func (at *Client) SetCustomClient(client *http.Client) {
|
||||
at.client = client
|
||||
}
|
||||
|
||||
// SetRateLimit rate limit setter for custom usage
|
||||
// Airtable limit is 5 requests per second (we use 4)
|
||||
// https://airtable.com/{yourDatabaseID}/api/docs#curl/ratelimits
|
||||
@@ -72,7 +77,7 @@ func (at *Client) rateLimit() {
|
||||
<-at.rateLimiter
|
||||
}
|
||||
|
||||
func (at *Client) get(db, table, recordID string, params url.Values, target interface{}) error {
|
||||
func (at *Client) get(ctx context.Context, db, table, recordID string, params url.Values, target interface{}) error {
|
||||
at.rateLimit()
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s", at.baseURL, db, table)
|
||||
@@ -80,7 +85,7 @@ func (at *Client) get(db, table, recordID string, params url.Values, target inte
|
||||
url += fmt.Sprintf("/%s", recordID)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create request: %w", err)
|
||||
}
|
||||
@@ -98,7 +103,7 @@ func (at *Client) get(db, table, recordID string, params url.Values, target inte
|
||||
return nil
|
||||
}
|
||||
|
||||
func (at *Client) post(db, table string, data, response interface{}) error {
|
||||
func (at *Client) post(ctx context.Context, db, table string, data, response interface{}) error {
|
||||
at.rateLimit()
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s", at.baseURL, db, table)
|
||||
@@ -108,7 +113,7 @@ func (at *Client) post(db, table string, data, response interface{}) error {
|
||||
return fmt.Errorf("cannot marshal body: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "POST", url, bytes.NewReader(body))
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create request: %w", err)
|
||||
}
|
||||
@@ -119,7 +124,7 @@ func (at *Client) post(db, table string, data, response interface{}) error {
|
||||
return at.do(req, response)
|
||||
}
|
||||
|
||||
func (at *Client) delete(db, table string, recordIDs []string, target interface{}) error {
|
||||
func (at *Client) delete(ctx context.Context, db, table string, recordIDs []string, target interface{}) error {
|
||||
at.rateLimit()
|
||||
|
||||
rawURL := fmt.Sprintf("%s/%s/%s", at.baseURL, db, table)
|
||||
@@ -129,7 +134,7 @@ func (at *Client) delete(db, table string, recordIDs []string, target interface{
|
||||
params.Add("records[]", recordID)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "DELETE", rawURL, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "DELETE", rawURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create request: %w", err)
|
||||
}
|
||||
@@ -147,7 +152,7 @@ func (at *Client) delete(db, table string, recordIDs []string, target interface{
|
||||
return nil
|
||||
}
|
||||
|
||||
func (at *Client) patch(db, table, data, response interface{}) error {
|
||||
func (at *Client) patch(ctx context.Context, db, table, data, response interface{}) error {
|
||||
at.rateLimit()
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s", at.baseURL, db, table)
|
||||
@@ -157,7 +162,7 @@ func (at *Client) patch(db, table, data, response interface{}) error {
|
||||
return fmt.Errorf("cannot marshal body: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "PATCH", url, bytes.NewReader(body))
|
||||
req, err := http.NewRequestWithContext(ctx, "PATCH", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create request: %w", err)
|
||||
}
|
||||
@@ -168,7 +173,7 @@ func (at *Client) patch(db, table, data, response interface{}) error {
|
||||
return at.do(req, response)
|
||||
}
|
||||
|
||||
func (at *Client) put(db, table, data, response interface{}) error {
|
||||
func (at *Client) put(ctx context.Context, db, table, data, response interface{}) error {
|
||||
at.rateLimit()
|
||||
|
||||
url := fmt.Sprintf("%s/%s/%s", at.baseURL, db, table)
|
||||
@@ -178,7 +183,7 @@ func (at *Client) put(db, table, data, response interface{}) error {
|
||||
return fmt.Errorf("cannot marshal body: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(context.Background(), "PUT", url, bytes.NewReader(body))
|
||||
req, err := http.NewRequestWithContext(ctx, "PUT", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create request: %w", err)
|
||||
}
|
||||
@@ -207,7 +212,7 @@ func (at *Client) do(req *http.Request, response interface{}) error {
|
||||
return makeHTTPClientError(url, resp)
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("HTTP Read error on response for %s: %w", url, err)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ package airtable
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -48,7 +48,7 @@ func makeHTTPClientError(url string, resp *http.Response) error {
|
||||
respStatusText = "The server could not process your request in time. The server could be temporarily unavailable, or it could have timed out processing your request. You should retry the request with backoffs."
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
resError = fmt.Errorf("HTTP request failure on %s:\n%d %s\n%s\n\nCannot parse body with err: %w",
|
||||
url, resp.StatusCode, resp.Status, respStatusText, err)
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
package airtable
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ func mockResponse(paths ...string) *httptest.Server {
|
||||
parts := []string{".", "testdata"}
|
||||
filename := filepath.Join(append(parts, paths...)...)
|
||||
|
||||
mockData, err := ioutil.ReadFile(filename)
|
||||
mockData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
29
record.go
29
record.go
@@ -5,7 +5,10 @@
|
||||
|
||||
package airtable
|
||||
|
||||
import "net/url"
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Record base time of airtable record fields.
|
||||
type Record struct {
|
||||
@@ -26,9 +29,15 @@ type Record struct {
|
||||
// GetRecord get record from table
|
||||
// https://airtable.com/{yourDatabaseID}/api/docs#curl/table:{yourTableName}:retrieve
|
||||
func (t *Table) GetRecord(recordID string) (*Record, error) {
|
||||
return t.GetRecordContext(context.Background(), recordID)
|
||||
}
|
||||
|
||||
// GetRecordContext get record from table
|
||||
// with custom context
|
||||
func (t *Table) GetRecordContext(ctx context.Context, recordID string) (*Record, error) {
|
||||
result := new(Record)
|
||||
|
||||
err := t.client.get(t.dbName, t.tableName, recordID, url.Values{}, result)
|
||||
err := t.client.get(ctx, t.dbName, t.tableName, recordID, url.Values{}, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -41,6 +50,12 @@ func (t *Table) GetRecord(recordID string) (*Record, error) {
|
||||
|
||||
// UpdateRecordPartial updates partial info on record.
|
||||
func (r *Record) UpdateRecordPartial(changedFields map[string]interface{}) (*Record, error) {
|
||||
return r.UpdateRecordPartialContext(context.Background(), changedFields)
|
||||
}
|
||||
|
||||
// UpdateRecordPartialContext updates partial info on record
|
||||
// with custom context
|
||||
func (r *Record) UpdateRecordPartialContext(ctx context.Context, changedFields map[string]interface{}) (*Record, error) {
|
||||
data := &Records{
|
||||
Records: []*Record{
|
||||
{
|
||||
@@ -51,7 +66,7 @@ func (r *Record) UpdateRecordPartial(changedFields map[string]interface{}) (*Rec
|
||||
}
|
||||
response := new(Records)
|
||||
|
||||
err := r.client.patch(r.table.dbName, r.table.tableName, data, response)
|
||||
err := r.client.patch(ctx, r.table.dbName, r.table.tableName, data, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -66,9 +81,15 @@ func (r *Record) UpdateRecordPartial(changedFields map[string]interface{}) (*Rec
|
||||
|
||||
// DeleteRecord delete one record.
|
||||
func (r *Record) DeleteRecord() (*Record, error) {
|
||||
return r.DeleteRecordContext(context.Background())
|
||||
}
|
||||
|
||||
// DeleteRecordContext delete one record
|
||||
// with custom context
|
||||
func (r *Record) DeleteRecordContext(ctx context.Context) (*Record, error) {
|
||||
response := new(Records)
|
||||
|
||||
err := r.client.delete(r.table.dbName, r.table.tableName, []string{r.ID}, response)
|
||||
err := r.client.delete(ctx, r.table.dbName, r.table.tableName, []string{r.ID}, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
39
table.go
39
table.go
@@ -6,6 +6,7 @@
|
||||
package airtable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
@@ -40,9 +41,15 @@ func (c *Client) GetTable(dbName, tableName string) *Table {
|
||||
// GetRecordsWithParams get records with url values params
|
||||
// https://airtable.com/{yourDatabaseID}/api/docs#curl/table:{yourTableName}:list
|
||||
func (t *Table) GetRecordsWithParams(params url.Values) (*Records, error) {
|
||||
return t.GetRecordsWithParamsContext(context.Background(), params)
|
||||
}
|
||||
|
||||
// GetRecordsWithParamsContext get records with url values params
|
||||
// with custom context
|
||||
func (t *Table) GetRecordsWithParamsContext(ctx context.Context, params url.Values) (*Records, error) {
|
||||
records := new(Records)
|
||||
|
||||
err := t.client.get(t.dbName, t.tableName, "", params, records)
|
||||
err := t.client.get(ctx, t.dbName, t.tableName, "", params, records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -58,9 +65,15 @@ func (t *Table) GetRecordsWithParams(params url.Values) (*Records, error) {
|
||||
// AddRecords method to add lines to table (up to 10 in one request)
|
||||
// https://airtable.com/{yourDatabaseID}/api/docs#curl/table:{yourTableName}:create
|
||||
func (t *Table) AddRecords(records *Records) (*Records, error) {
|
||||
return t.AddRecordsContext(context.Background(), records)
|
||||
}
|
||||
|
||||
// AddRecordsContext method to add lines to table (up to 10 in one request)
|
||||
// with custom context
|
||||
func (t *Table) AddRecordsContext(ctx context.Context, records *Records) (*Records, error) {
|
||||
result := new(Records)
|
||||
|
||||
err := t.client.post(t.dbName, t.tableName, records, result)
|
||||
err := t.client.post(ctx, t.dbName, t.tableName, records, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -75,9 +88,14 @@ func (t *Table) AddRecords(records *Records) (*Records, error) {
|
||||
|
||||
// UpdateRecords full update records.
|
||||
func (t *Table) UpdateRecords(records *Records) (*Records, error) {
|
||||
return t.UpdateRecordsContext(context.Background(), records)
|
||||
}
|
||||
|
||||
// UpdateRecordsContext full update records with custom context.
|
||||
func (t *Table) UpdateRecordsContext(ctx context.Context, records *Records) (*Records, error) {
|
||||
response := new(Records)
|
||||
|
||||
err := t.client.put(t.dbName, t.tableName, records, response)
|
||||
err := t.client.put(ctx, t.dbName, t.tableName, records, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -92,9 +110,14 @@ func (t *Table) UpdateRecords(records *Records) (*Records, error) {
|
||||
|
||||
// UpdateRecordsPartial partial update records.
|
||||
func (t *Table) UpdateRecordsPartial(records *Records) (*Records, error) {
|
||||
return t.UpdateRecordsPartialContext(context.Background(), records)
|
||||
}
|
||||
|
||||
// UpdateRecordsPartialContext partial update records with custom context.
|
||||
func (t *Table) UpdateRecordsPartialContext(ctx context.Context, records *Records) (*Records, error) {
|
||||
response := new(Records)
|
||||
|
||||
err := t.client.patch(t.dbName, t.tableName, records, response)
|
||||
err := t.client.patch(ctx, t.dbName, t.tableName, records, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -110,9 +133,15 @@ func (t *Table) UpdateRecordsPartial(records *Records) (*Records, error) {
|
||||
// DeleteRecords delete records by recordID
|
||||
// up to 10 ids in one request.
|
||||
func (t *Table) DeleteRecords(recordIDs []string) (*Records, error) {
|
||||
return t.DeleteRecordsContext(context.Background(), recordIDs)
|
||||
}
|
||||
|
||||
// DeleteRecordsContext delete records by recordID
|
||||
// with custom context
|
||||
func (t *Table) DeleteRecordsContext(ctx context.Context, recordIDs []string) (*Records, error) {
|
||||
response := new(Records)
|
||||
|
||||
err := t.client.delete(t.dbName, t.tableName, recordIDs, response)
|
||||
err := t.client.delete(ctx, t.dbName, t.tableName, recordIDs, response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user