Replace/UpsertReplace support

This commit is contained in:
Ian Gulliver
2024-06-23 16:32:28 -07:00
parent 6abf2d051e
commit fd97e6ecca
6 changed files with 89 additions and 25 deletions

10
base.go
View File

@@ -6,9 +6,9 @@ import (
)
type Base struct {
ID string `json:"id"`
Name string `json:"name"`
PermissionLevel string `json:"permissionLevel"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
PermissionLevel *string `json:"permissionLevel,omitempty"`
c *Client
}
@@ -27,7 +27,7 @@ func (c *Client) GetBaseByName(ctx context.Context, name string) (*Base, error)
}
for _, base := range bases {
if base.Name == name {
if *base.Name == name {
return base, nil
}
}
@@ -36,5 +36,5 @@ func (c *Client) GetBaseByName(ctx context.Context, name string) (*Base, error)
}
func (b Base) String() string {
return b.Name
return *b.Name
}

View File

@@ -47,3 +47,7 @@ func (c *Client) SetRateLimit(rateLimit int) {
func (c *Client) waitForRateLimit() {
<-c.rateLimiter
}
func P(s string) *string {
return &s
}

View File

@@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
@@ -63,7 +64,8 @@ func do[T any](ctx context.Context, c *Client, method, path string, params url.V
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("http error: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
errBody, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("http error: %d %s (%s)", resp.StatusCode, http.StatusText(resp.StatusCode), string(errBody))
}
dec := json.NewDecoder(resp.Body)

View File

@@ -6,10 +6,9 @@ import (
)
type Record struct {
ID string `json:"id,omitempty"`
ID *string `json:"id,omitempty"`
Fields map[string]any `json:"fields"`
CreatedTime string `json:"createdTime,omitempty"`
Deleted bool `json:"deleted,omitempty"`
CreatedTime *string `json:"createdTime,omitempty"`
c *Client
b *Base
@@ -20,7 +19,7 @@ type ListRecordOptions struct {
}
func (t *Table) ListRecords(ctx context.Context, opts *ListRecordOptions) ([]*Record, error) {
return listAll[Record](ctx, t.c, fmt.Sprintf("%s/%s", t.b.ID, t.ID), nil, "records", func(r *Record) error {
return listAll[Record](ctx, t.c, fmt.Sprintf("%s/%s", *t.b.ID, *t.ID), nil, "records", func(r *Record) error {
r.c = t.c
r.b = t.b
r.t = t
@@ -28,6 +27,53 @@ func (t *Table) ListRecords(ctx context.Context, opts *ListRecordOptions) ([]*Re
})
}
func (r Record) String() string {
return r.Fields[r.t.Fields[0].Name].(string)
type updateRecordsRequest struct {
PerformUpsert *performUpsertRequest `json:"performUpsert,omitempty"`
Records []*Record `json:"records"`
}
type performUpsertRequest struct {
FieldsToMergeOn []string `json:"fieldsToMergeOn"`
}
type updateRecordsResponse struct {
Records []*Record `json:"records"`
}
func (t *Table) ReplaceRecords(ctx context.Context, records []*Record, matchFields []string) ([]*Record, error) {
ret := []*Record{}
req := &updateRecordsRequest{}
if len(matchFields) > 0 {
req.PerformUpsert = &performUpsertRequest{
FieldsToMergeOn: matchFields,
}
}
for _, chk := range chunk(records, 10) {
req.Records = chk
resp, err := put[updateRecordsResponse](ctx, t.c, fmt.Sprintf("%s/%s", *t.b.ID, *t.ID), req)
if err != nil {
return nil, err
}
for _, rec := range resp.Records {
rec.c = t.c
rec.b = t.b
rec.t = t
ret = append(ret, rec)
}
}
return ret, nil
}
func (r Record) String() string {
if r.ID == nil {
return fmt.Sprintf("(%s [nil])", r.Fields[*r.t.Fields[0].Name].(string))
} else {
return fmt.Sprintf("(%s [%s])", r.Fields[*r.t.Fields[0].Name].(string), *r.ID)
}
}

View File

@@ -6,10 +6,10 @@ import (
)
type Table struct {
ID string `json:"id"`
PrimaryFieldID string `json:"primaryFieldId"`
Name string `json:"name"`
Description string `json:"description"`
ID *string `json:"id,omitempty"`
PrimaryFieldID *string `json:"primaryFieldId,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Fields []*Field `json:"fields"`
Views []*View `json:"views"`
@@ -18,21 +18,21 @@ type Table struct {
}
type Field struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Description string `json:"description"`
ID *string `json:"id"`
Type *string `json:"type"`
Name *string `json:"name"`
Description *string `json:"description"`
Options map[string]any `json:"options"`
}
type View struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
ID *string `json:"id"`
Type *string `json:"type"`
Name *string `json:"name"`
}
func (b *Base) ListTables(ctx context.Context) ([]*Table, error) {
return listAll[Table](ctx, b.c, fmt.Sprintf("meta/bases/%s/tables", b.ID), nil, "tables", func(t *Table) error {
return listAll[Table](ctx, b.c, fmt.Sprintf("meta/bases/%s/tables", *b.ID), nil, "tables", func(t *Table) error {
t.c = b.c
t.b = b
return nil
@@ -46,7 +46,7 @@ func (b *Base) GetTableByName(ctx context.Context, name string) (*Table, error)
}
for _, table := range tables {
if table.Name == name {
if *table.Name == name {
return table, nil
}
}

12
util.go Normal file
View File

@@ -0,0 +1,12 @@
package airtable
func chunk[T any](items []T, chunkSize int) [][]T {
chunks := [][]T{}
for chunkSize < len(items) {
chunks = append(chunks, items[0:chunkSize:chunkSize])
items = items[chunkSize:]
}
return append(chunks, items)
}