Skip to content

Commit

Permalink
initial airtable version
Browse files Browse the repository at this point in the history
  • Loading branch information
moul committed Oct 6, 2018
1 parent b8ebb1f commit 6888113
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
147 changes: 147 additions & 0 deletions cmd_airtable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package main

import (
"encoding/json"
"time"

airtable "github.com/fabioberger/airtable-go"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
)

type airtableOptions struct {
AirtableTableName string `mapstructure:"airtable-table-name"`
AirtableBaseID string `mapstructure:"airtable-base-id"`
AirtableToken string `mapstructure:"airtable-token"`
Targets []string
}

func (opts airtableOptions) String() string {
out, _ := json.Marshal(opts)
return string(out)
}

func airtableSetupFlags(flags *pflag.FlagSet, opts *airtableOptions) {
flags.StringVarP(&opts.AirtableTableName, "airtable-table-name", "", "Issues and PRs", "Airtable table name")
flags.StringVarP(&opts.AirtableBaseID, "airtable-base-id", "", "", "Airtable base ID")
flags.StringVarP(&opts.AirtableToken, "airtable-token", "", "", "Airtable token")
viper.BindPFlags(flags)
}

func newAirtableCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "airtable",
}
cmd.AddCommand(newAirtableSyncCommand())
return cmd
}

func newAirtableSyncCommand() *cobra.Command {
opts := &airtableOptions{}
cmd := &cobra.Command{
Use: "sync",
RunE: func(cmd *cobra.Command, args []string) error {
if err := viper.Unmarshal(opts); err != nil {
return err
}
opts.Targets = args
return airtableSync(opts)
},
}
airtableSetupFlags(cmd.Flags(), opts)
return cmd
}

func airtableSync(opts *airtableOptions) error {
issues, err := loadIssues(db, nil)
if err != nil {
return errors.Wrap(err, "failed to load issues")
}
if err := issues.prepare(); err != nil {
return errors.Wrap(err, "failed to prepare issues")
}
issues.filterByTargets(opts.Targets)
logger().Debug("fetch db entries", zap.Int("count", len(issues)))

at, err := airtable.New(opts.AirtableToken, opts.AirtableBaseID)
if err != nil {
return err
}

alreadyInAirtable := map[string]bool{}

records := []airtableRecord{}
if err := at.ListRecords(opts.AirtableTableName, &records); err != nil {
return err
}
logger().Debug("fetched airtable records", zap.Int("count", len(records)))

for _, record := range records {
alreadyInAirtable[record.Fields.ID] = true
if issue, found := issues[record.Fields.ID]; !found {
logger().Debug("destroying airtable record", zap.String("ID", record.Fields.ID))
if err := at.DestroyRecord(opts.AirtableTableName, record.ID); err != nil {
return errors.Wrap(err, "failed to destroy record")
}
} else {
if issue.Hidden {
continue
}
// FIXME: check if entry changed before updating
logger().Debug("updating airtable record", zap.String("ID", issue.URL))
if err := at.UpdateRecord(opts.AirtableTableName, record.ID, issue.ToAirtableRecord().Fields.Map(), &record); err != nil {
return errors.Wrap(err, "failed to update record")
}
}
}

for _, issue := range issues {
if issue.Hidden {
continue
}
if _, found := alreadyInAirtable[issue.URL]; found {
continue
}
logger().Debug("creating airtable record", zap.String("ID", issue.URL))
if err := at.CreateRecord(opts.AirtableTableName, issue.ToAirtableRecord()); err != nil {
return err
}
}
return nil
}

func (i Issue) ToAirtableRecord() airtableRecord {
return airtableRecord{
ID: "",
Fields: airtableIssue{
ID: i.URL,
Created: i.CreatedAt,
Updated: i.UpdatedAt,
Title: i.Title,
},
}
}

type airtableRecord struct {
ID string `json:"id,omitempty"`
Fields airtableIssue `json:"fields,omitempty"`
}

type airtableIssue struct {
ID string
Created time.Time
Updated time.Time
Title string
}

func (a airtableIssue) Map() map[string]interface{} {
return map[string]interface{}{
"ID": a.ID,
"Created": a.Created,
"Updated": a.Updated,
"Title": a.Title,
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/awalterschulze/gographviz v0.0.0-20180927133620-e69668a01397
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 // indirect
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
github.com/fabioberger/airtable-go v3.1.0+incompatible // indirect
github.com/go-chi/chi v3.3.3+incompatible
github.com/go-chi/docgen v1.0.2 // indirect
github.com/go-chi/render v1.0.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6 h1:BZGp1dbKF
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fabioberger/airtable-go v3.1.0+incompatible h1:n5dw+HWBc+hytrVL75xe94EGt7FtNFGDII1tNoWTCAE=
github.com/fabioberger/airtable-go v3.1.0+incompatible/go.mod h1:EoKuSh7EefzhMCyVr6iXPlgFzDgHyZCZ3E5Sg8Cy9GM=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-chi/chi v3.3.3+incompatible h1:KHkmBEMNkwKuK4FdQL7N2wOeB9jnIx7jR5wsuSBEFI8=
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func newRootCommand() *cobra.Command {
newRunCommand(),
newDBCommand(),
newWebCommand(),
newAirtableCommand(),
)
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
Expand Down

0 comments on commit 6888113

Please sign in to comment.