diff --git a/cmd/cmd_airtable.go b/cmd/cmd_airtable.go index 39e90ac5f..c5a49a8df 100644 --- a/cmd/cmd_airtable.go +++ b/cmd/cmd_airtable.go @@ -109,47 +109,42 @@ func airtableSync(opts *airtableOptions) error { // unique entries features := make([]map[string]repo.Feature, airtabledb.NumTables) + for i, _ := range features { + features[i] = make(map[string]repo.Feature) + } for _, issue := range filtered { // providers - //providerMap[issue.Repository.Provider.ID] = issue.Repository.Provider features[airtabledb.ProviderIndex][issue.Repository.Provider.ID] = issue.Repository.Provider // labels for _, label := range issue.Labels { - //labelMap[label.ID] = label features[airtabledb.LabelIndex][label.ID] = label } // accounts if issue.Repository.Owner != nil { - //accountMap[issue.Repository.Owner.ID] = issue.Repository.Owner features[airtabledb.AccountIndex][issue.Repository.Owner.ID] = issue.Repository.Owner } - //accountMap[issue.Author.ID] = issue.Author + features[airtabledb.AccountIndex][issue.Author.ID] = issue.Author for _, assignee := range issue.Assignees { - //accountMap[assignee.ID] = assignee features[airtabledb.AccountIndex][assignee.ID] = assignee } if issue.Milestone != nil && issue.Milestone.Creator != nil { - //accountMap[issue.Milestone.Creator.ID] = issue.Milestone.Creator features[airtabledb.AccountIndex][issue.Milestone.Creator.ID] = issue.Milestone.Creator } // repositories - //repositoryMap[issue.Repository.ID] = issue.Repository features[airtabledb.RepositoryIndex][issue.Repository.ID] = issue.Repository // FIXME: find external repositories based on depends-on links // milestones if issue.Milestone != nil { - //milestoneMap[issue.Milestone.ID] = issue.Milestone features[airtabledb.MilestoneIndex][issue.Milestone.ID] = issue.Milestone } // issue - //issueMap[issue.ID] = issue features[airtabledb.IssueIndex][issue.ID] = issue // FIXME: find external issues based on depends-on links } @@ -165,8 +160,7 @@ func airtableSync(opts *airtableOptions) error { cache := airtabledb.NewDB() for tableKind, tableName := range opts.TableNames { table := at.Table(tableName) - records := cache.Tables[tableKind] - if err := table.List(&records, &airtable.Options{}); err != nil { + if err := cache.Tables[tableKind].Fetch(table); err != nil { return err } } @@ -181,20 +175,21 @@ func airtableSync(opts *airtableOptions) error { for _, dbEntry := range featureMap { matched := false dbRecord := dbEntry.ToRecord(cache) - for idx, atEntry := range cache.Tables[tableKind] { - if atEntry.Fields.ID == dbEntry.GetID() { - if atEntry.Equals(dbRecord) { - cache.Tables[tableKind][idx].State = airtabledb.StateUnchanged + for idx := 0; idx < cache.Tables[tableKind].Len(); idx ++ { + t := cache.Tables[tableKind] + if t.GetFieldID(idx) == dbEntry.GetID() { + if t.RecordsEqual(idx, dbRecord) { + t.SetState(idx, airtabledb.StateUnchanged) } else { - cache.Tables[tableKind][idx].Fields = dbRecord.Fields - cache.Tables[tableKind][idx].State = airtabledb.StateChanged + t.CopyFields(idx, dbRecord) + t.SetState(idx, airtabledb.StateChanged) } matched = true break } } if !matched { - unmatched.Tables[tableKind] = append(unmatched.Tables[tableKind], dbRecord) + unmatched.Tables[tableKind].Append(dbRecord) } } } @@ -204,28 +199,30 @@ func airtableSync(opts *airtableOptions) error { // for tableKind, tableName := range opts.TableNames { table := at.Table(tableName) - for _, entry := range unmatched.Tables[tableKind] { - zap.L().Debug("create airtable entry", zap.String("type", tableName), zap.Stringer("entry", entry)) - if err := table.Create(&entry); err != nil { + ut := unmatched.Tables[tableKind] + ct := cache.Tables[tableKind] + for i := 0; i < ut.Len(); i++ { + zap.L().Debug("create airtable entry", zap.String("type", tableName), zap.String("entry", ut.StringAt(i))) + if err := table.Create(ut.GetPtr(i)); err != nil { return err } - entry.State = airtabledb.StateNew - cache.Tables[tableKind] = append(cache.Tables[tableKind], entry) + ut.SetState(i, airtabledb.StateNew) + ct.Append(ut.Get(i)) } - for _, entry := range cache.Tables[tableKind] { + for i := 0; i < ct.Len(); i++ { var err error - switch entry.State { + switch ct.GetState(i) { case airtabledb.StateUnknown: - err = table.Delete(&entry) - zap.L().Debug("delete airtable entry", zap.String("type", tableName), zap.Stringer("entry", entry), zap.Error(err)) + err = table.Delete(ct.GetPtr(i)) + zap.L().Debug("delete airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) case airtabledb.StateChanged: - err = table.Update(&entry) - zap.L().Debug("update airtable entry", zap.String("type", tableName), zap.Stringer("entry", entry), zap.Error(err)) + err = table.Update(ct.GetPtr(i)) + zap.L().Debug("update airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) case airtabledb.StateUnchanged: - zap.L().Debug("unchanged airtable entry", zap.String("type", tableName), zap.Stringer("entry", entry), zap.Error(err)) + zap.L().Debug("unchanged airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) // do nothing case airtabledb.StateNew: - zap.L().Debug("new airtable entry", zap.String("type", tableName), zap.Stringer("entry", entry), zap.Error(err)) + zap.L().Debug("new airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) // do nothing } } @@ -236,8 +233,9 @@ func airtableSync(opts *airtableOptions) error { // for tableKind, tableName := range opts.TableNames { fmt.Println("-------", tableName) - for _, entry := range cache.Tables[tableKind] { - fmt.Println(entry.ID, airtabledb.StateString[entry.State], entry.Fields.ID) + ct := cache.Tables[tableKind] + for i := 0; i < ct.Len(); i++ { + fmt.Println(ct.GetID(i), airtabledb.StateString[ct.GetState(i)], ct.GetFieldID(i)) } } fmt.Println("-------") diff --git a/pkg/airtabledb/airtabledb.go b/pkg/airtabledb/airtabledb.go index 67e0c6ead..a57510db4 100644 --- a/pkg/airtabledb/airtabledb.go +++ b/pkg/airtabledb/airtabledb.go @@ -9,120 +9,159 @@ import ( "github.com/brianloveswords/airtable" ) -type FeatureFields interface { +type Record interface { String() string } -type Record struct { - State State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - - Fields struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created-at"` - UpdatedAt time.Time `json:"updated-at"` - Errors string `json:"errors"` - - Feature FeatureFields - } `json:"fields,omitempty"` -} - -func (r Record) Equals(other Record) bool { - if r.Fields.ID != other.Fields.ID { - return false +func (t Table) RecordsEqual(idx int, b Record) bool { + sf, ok := reflect.TypeOf(t.Get(idx)).FieldByName("Fields") + if !ok { + panic("No struct field Fields in Record") } - if !isSameAirtableDate(r.Fields.CreatedAt, other.Fields.CreatedAt) { - return false - } - if !isSameAirtableDate(r.Fields.UpdatedAt, other.Fields.UpdatedAt) { - return false - } - if r.Fields.Errors != other.Fields.Errors { - return false - } - rV := reflect.ValueOf(r.Fields.Feature) - oV := reflect.ValueOf(other.Fields.Feature) - if rV.Type() != oV.Type() { - return false - } - if rV.NumField() != oV.NumField() { + aTF := sf.Type + aVF := reflect.ValueOf(t.Get(idx)).FieldByName("Fields") + bVF := reflect.ValueOf(b).FieldByName("Fields") + + if aVF.NumField() != bVF.NumField() { return false } - for i := 0; i < rV.NumField(); i++ { - rF := rV.Field(i) - oF := oV.Field(i) - if rF.Type() != oF.Type() { + for i := 0; i < aVF.NumField(); i++ { + aiSF := aTF.Field(i) + aiF := aVF.Field(i) + biF := bVF.FieldByName(aiSF.Name) + if aiF.Type() != biF.Type() { return false } - if rF.Type().String() == "time.Time" { - if !isSameAirtableDate(rF.Interface().(time.Time), oF.Interface().(time.Time)) { + if aiF.Type().String() == "time.Time" { + if !isSameAirtableDate(aiF.Interface().(time.Time), biF.Interface().(time.Time)) { return false } - } - if rF.Type().String() == "[]string" { - a, b := rF.Interface().([]string), oF.Interface().([]string) - if a == nil { - a = []string{} + } else if aiF.Type().String() == "[]string" { + aS, bS := aiF.Interface().([]string), biF.Interface().([]string) + if aS == nil { + aS = []string{} } - if b == nil { - b = []string{} + if bS == nil { + bS = []string{} } - sort.Strings(a) - sort.Strings(b) - if !reflect.DeepEqual(a, b) { + sort.Strings(aS) + sort.Strings(bS) + if !reflect.DeepEqual(aS, bS) { + return false + } + continue + } else { + if !reflect.DeepEqual(aiF.Interface(), biF.Interface()) { return false } - } - if !reflect.DeepEqual(rF.Interface(), oF.Interface()) { - return false } } return true } -func (r Record) String() string { - out, _ := json.Marshal(r) - return string(out) +type Table struct { + elems interface{} +} + +func (t Table) SetState(idx int, state State) { + s := reflect.ValueOf(t.elems).Elem().Index(idx).FieldByName("State") + s.SetInt(int64(state)) +} + +func (t Table) GetState(idx int) State { + return State(reflect.ValueOf(t.elems).Elem().Index(idx).FieldByName("State").Int()) +} + +func (t Table) CopyFields(idx int, src interface{}) { + dstF := reflect.ValueOf(t.elems).Elem().Index(idx).FieldByName("Fields") + srcF := reflect.ValueOf(src).FieldByName("Fields") + dstF.Set(srcF) +} + +func (t Table) GetFieldID(idx int) string { + return reflect.ValueOf(t.elems).Elem().Index(idx).FieldByName("Fields").FieldByName("ID").String() +} + +func (t Table) GetID(idx int) string { + return reflect.ValueOf(t.elems).Elem().Index(idx).FieldByName("ID").String() +} + +func (t Table) Len() int { + return reflect.ValueOf(t.elems).Elem().Len() } -type Records []Record +func (t Table) Append(r interface{}) { + a := reflect.Append(reflect.ValueOf(t.elems).Elem(), reflect.ValueOf(r)) + reflect.ValueOf(t.elems).Elem().Set(a) +} + +func (t Table) Fetch(at airtable.Table) error { + return at.List(t.elems, &airtable.Options{}) +} -func (r Records) FindByID(id string) string { - for _, record := range r { - if record.Fields.ID == id { - return record.ID +func (t Table) FindByID(id string) string { + slice := reflect.ValueOf(t.elems).Elem() + for i := 0; i < slice.Len(); i++ { + record := slice.Index(i) + fieldID := record.FieldByName("Fields").FieldByName("ID").String() + if fieldID == id { + return record.FieldByName("ID").String() } } return "" } -type base struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created-at"` - UpdatedAt time.Time `json:"updated-at"` - Errors string `json:"errors"` +func (t Table) GetPtr(idx int) interface{} { + return reflect.ValueOf(t.elems).Elem().Index(idx).Addr().Interface() } -type State int +func (t Table) Get(idx int) interface{} { + return reflect.ValueOf(t.elems).Elem().Index(idx).Interface() +} + +func (t Table) StringAt(idx int) string { + out := reflect.ValueOf(t.elems).Elem().Index(idx).MethodByName("String").Call(nil) + return out[0].String() +} type DB struct { - Tables []Records + Tables []Table } func NewDB() DB { - return DB{ - Tables: make([]Records, NumTables), + db := DB{ + Tables: make([]Table, NumTables), } + db.Tables[IssueIndex].elems = &[]IssueRecord{} + db.Tables[RepositoryIndex].elems = &[]RepositoryRecord{} + db.Tables[AccountIndex].elems = &[]AccountRecord{} + db.Tables[LabelIndex].elems = &[]LabelRecord{} + db.Tables[MilestoneIndex].elems = &[]MilestoneRecord{} + db.Tables[ProviderIndex].elems = &[]ProviderRecord{} + if len(db.Tables) != NumTables { + panic("missing an airtabledb Table") + } + return db } +type Base struct { + ID string `json:"id"` + CreatedAt time.Time `json:"created-at"` + UpdatedAt time.Time `json:"updated-at"` + Errors string `json:"errors"` +} + +type State int + +// Unfortunately, the order matters here. +// We must first compute Records which are referenced by other Records... const ( - IssueIndex = iota - RepositoryIndex - AccountIndex + ProviderIndex = iota LabelIndex + AccountIndex + RepositoryIndex MilestoneIndex - ProviderIndex + IssueIndex NumTables ) @@ -147,9 +186,20 @@ var ( // type ProviderRecord struct { - // specific - URL string `json:"url"` - Driver string `json:"driver"` + State State `json:"-"` // internal + + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + Driver string `json:"driver"` + + // relationship + // n/a + } `json:"fields,omitempty"` } func (r ProviderRecord) String() string { @@ -160,15 +210,24 @@ func (r ProviderRecord) String() string { // // label // + type LabelRecord struct { - // specific - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - Description string `json:"description"` - - // relationship - // n/a + State State `json:"-"` // internal + + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + Name string `json:"name"` + Color string `json:"color"` + Description string `json:"description"` + + // relationship + // n/a + } `json:"fields,omitempty"` } func (r LabelRecord) String() string { @@ -181,20 +240,28 @@ func (r LabelRecord) String() string { // type AccountRecord struct { - // specific - URL string `json:"url"` - Login string `json:"login"` - FullName string `json:"fullname"` - Type string `json:"type"` - Bio string `json:"bio"` - Location string `json:"location"` - Company string `json:"company"` - Blog string `json:"blog"` - Email string `json:"email"` - AvatarURL string `json:"avatar-url"` - - // relationships - Provider []string `json:"provider"` + State State `json:"-"` // internal + + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + Login string `json:"login"` + FullName string `json:"fullname"` + Type string `json:"type"` + Bio string `json:"bio"` + Location string `json:"location"` + Company string `json:"company"` + Blog string `json:"blog"` + Email string `json:"email"` + AvatarURL string `json:"avatar-url"` + + // relationships + Provider []string `json:"provider"` + } `json:"fields,omitempty"` } func (r AccountRecord) String() string { @@ -207,17 +274,25 @@ func (r AccountRecord) String() string { // type RepositoryRecord struct { - // specific - URL string `json:"url"` - Title string `json:"title"` - Description string `json:"description"` - Homepage string `json:"homepage"` - PushedAt time.Time `json:"pushed-at"` - IsFork bool `json:"is-fork"` - - // relationships - Provider []string `json:"provider"` - Owner []string `json:"owner"` + State State `json:"-"` // internal + + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + Title string `json:"title"` + Description string `json:"description"` + Homepage string `json:"homepage"` + PushedAt time.Time `json:"pushed-at"` + IsFork bool `json:"is-fork"` + + // relationships + Provider []string `json:"provider"` + Owner []string `json:"owner"` + } `json:"fields,omitempty"` } func (r RepositoryRecord) String() string { @@ -230,15 +305,24 @@ func (r RepositoryRecord) String() string { // type MilestoneRecord struct { - URL string `json:"url"` - Title string `json:"title"` - Description string `json:"description"` - ClosedAt time.Time `json:"closed-at"` - DueOn time.Time `json:"due-on"` - - // relationships - Creator []string `json:"creator"` - Repository []string `json:"repository"` + State State `json:"-"` // internal + + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + Title string `json:"title"` + Description string `json:"description"` + ClosedAt time.Time `json:"closed-at"` + DueOn time.Time `json:"due-on"` + + // relationships + Creator []string `json:"creator"` + Repository []string `json:"repository"` + } `json:"fields,omitempty"` } func (r MilestoneRecord) String() string { @@ -251,33 +335,41 @@ func (r MilestoneRecord) String() string { // type IssueRecord struct { - URL string `json:"url"` - CompletedAt time.Time `json:"completed-at"` - Title string `json:"title"` - State string `json:"state"` - Body string `json:"body"` - IsPR bool `json:"is-pr"` - IsLocked bool `json:"is-locked"` - Comments int `json:"comments"` - Upvotes int `json:"upvotes"` - Downvotes int `json:"downvotes"` - IsOrphan bool `json:"is-orphan"` - IsHidden bool `json:"is-hidden"` - Weight int `json:"weight"` - IsEpic bool `json:"is-epic"` - HasEpic bool `json:"has-epic"` - - // relationships - Repository []string `json:"repository"` - Milestone []string `json:"milestone"` - Author []string `json:"author"` - Labels []string `json:"labels"` - Assignees []string `json:"assignees"` - //Parents []string `json:"-"` - //Children []string `json:"-"` - //Duplicates []string `json:"-"` -} + State State `json:"-"` // internal + airtable.Record // provides ID, CreatedTime + Fields struct { + // base + Base + + // specific + URL string `json:"url"` + CompletedAt time.Time `json:"completed-at"` + Title string `json:"title"` + State string `json:"state"` + Body string `json:"body"` + IsPR bool `json:"is-pr"` + IsLocked bool `json:"is-locked"` + Comments int `json:"comments"` + Upvotes int `json:"upvotes"` + Downvotes int `json:"downvotes"` + IsOrphan bool `json:"is-orphan"` + IsHidden bool `json:"is-hidden"` + Weight int `json:"weight"` + IsEpic bool `json:"is-epic"` + HasEpic bool `json:"has-epic"` + + // relationships + Repository []string `json:"repository"` + Milestone []string `json:"milestone"` + Author []string `json:"author"` + Labels []string `json:"labels"` + Assignees []string `json:"assignees"` + //Parents []string `json:"-"` + //Children []string `json:"-"` + //Duplicates []string `json:"-"` + } `json:"fields,omitempty"` +} func (r IssueRecord) String() string { out, _ := json.Marshal(r) return string(out) diff --git a/pkg/repo/models.go b/pkg/repo/models.go index 3aae990fd..f909148d7 100644 --- a/pkg/repo/models.go +++ b/pkg/repo/models.go @@ -53,8 +53,7 @@ type Repository struct { } func (p Repository) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.RepositoryRecord{} + record := airtabledb.RepositoryRecord{} // base record.Fields.ID = p.ID @@ -63,20 +62,19 @@ func (p Repository) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.Title = p.Title - features.Description = p.Description - features.Homepage = p.Homepage - features.PushedAt = p.PushedAt - features.IsFork = p.IsFork + record.Fields.URL = p.URL + record.Fields.Title = p.Title + record.Fields.Description = p.Description + record.Fields.Homepage = p.Homepage + record.Fields.PushedAt = p.PushedAt + record.Fields.IsFork = p.IsFork // relationships - features.Provider = []string{cache.Tables[airtabledb.ProviderIndex].FindByID(p.Provider.ID)} + record.Fields.Provider = []string{cache.Tables[airtabledb.ProviderIndex].FindByID(p.Provider.ID)} if p.Owner != nil { - features.Owner = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Owner.ID)} + record.Fields.Owner = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Owner.ID)} } - record.Fields.Feature = features return record } @@ -106,8 +104,7 @@ type Provider struct { } func (p Provider) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.ProviderRecord{} + record := airtabledb.ProviderRecord{} // base record.Fields.ID = p.ID @@ -116,13 +113,12 @@ func (p Provider) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.Driver = p.Driver + record.Fields.URL = p.URL + record.Fields.Driver = p.Driver // relationships // n/a - record.Fields.Feature = features return record } @@ -155,9 +151,7 @@ type Milestone struct { } func (p Milestone) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.MilestoneRecord{} - + record := airtabledb.MilestoneRecord{} // base record.Fields.ID = p.ID record.Fields.CreatedAt = p.CreatedAt @@ -165,22 +159,20 @@ func (p Milestone) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.Title = p.Title - features.Description = p.Description - features.ClosedAt = p.ClosedAt - features.DueOn = p.DueOn + record.Fields.URL = p.URL + record.Fields.Title = p.Title + record.Fields.Description = p.Description + record.Fields.ClosedAt = p.ClosedAt + record.Fields.DueOn = p.DueOn // relationships if p.Creator != nil { - features.Creator = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Creator.ID)} + record.Fields.Creator = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Creator.ID)} } if p.Repository != nil { - features.Repository = []string{cache.Tables[airtabledb.RepositoryIndex].FindByID(p.Repository.ID)} + record.Fields.Repository = []string{cache.Tables[airtabledb.RepositoryIndex].FindByID(p.Repository.ID)} } - record.Fields.Feature = features - return record } @@ -238,9 +230,7 @@ func (i Issue) String() string { } func (p Issue) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.IssueRecord{} - + record := airtabledb.IssueRecord{} // base record.Fields.ID = p.ID record.Fields.CreatedAt = p.CreatedAt @@ -248,38 +238,37 @@ func (p Issue) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.CompletedAt = p.CompletedAt - features.Title = p.Title - features.State = p.State - features.Body = p.Body - features.IsPR = p.IsPR - features.IsLocked = p.IsLocked - features.Comments = p.Comments - features.Upvotes = p.Upvotes - features.Downvotes = p.Downvotes - features.IsOrphan = p.IsOrphan - features.IsHidden = p.IsHidden - features.Weight = p.Weight - features.IsEpic = p.IsEpic - features.HasEpic = p.HasEpic + record.Fields.URL = p.URL + record.Fields.CompletedAt = p.CompletedAt + record.Fields.Title = p.Title + record.Fields.State = p.State + record.Fields.Body = p.Body + record.Fields.IsPR = p.IsPR + record.Fields.IsLocked = p.IsLocked + record.Fields.Comments = p.Comments + record.Fields.Upvotes = p.Upvotes + record.Fields.Downvotes = p.Downvotes + record.Fields.IsOrphan = p.IsOrphan + record.Fields.IsHidden = p.IsHidden + record.Fields.Weight = p.Weight + record.Fields.IsEpic = p.IsEpic + record.Fields.HasEpic = p.HasEpic // relationships - features.Repository = []string{cache.Tables[airtabledb.RepositoryIndex].FindByID(p.Repository.ID)} + record.Fields.Repository = []string{cache.Tables[airtabledb.RepositoryIndex].FindByID(p.Repository.ID)} if p.Milestone != nil { - features.Milestone = []string{cache.Tables[airtabledb.MilestoneIndex].FindByID(p.Milestone.ID)} + record.Fields.Milestone = []string{cache.Tables[airtabledb.MilestoneIndex].FindByID(p.Milestone.ID)} } - features.Author = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Author.ID)} - features.Labels = []string{} + record.Fields.Author = []string{cache.Tables[airtabledb.AccountIndex].FindByID(p.Author.ID)} + record.Fields.Labels = []string{} for _, label := range p.Labels { - features.Labels = append(features.Labels, cache.Tables[airtabledb.LabelIndex].FindByID(label.ID)) + record.Fields.Labels = append(record.Fields.Labels, cache.Tables[airtabledb.LabelIndex].FindByID(label.ID)) } - features.Assignees = []string{} + record.Fields.Assignees = []string{} for _, assignee := range p.Assignees { - features.Assignees = append(features.Assignees, cache.Tables[airtabledb.AccountIndex].FindByID(assignee.ID)) + record.Fields.Assignees = append(record.Fields.Assignees, cache.Tables[airtabledb.AccountIndex].FindByID(assignee.ID)) } - record.Fields.Feature = features return record } @@ -298,8 +287,7 @@ type Label struct { } func (p Label) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.LabelRecord{} + record := airtabledb.LabelRecord{} // base record.Fields.ID = p.ID @@ -308,16 +296,14 @@ func (p Label) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.Name = p.Name - features.Color = p.Color - features.Description = p.Description + record.Fields.URL = p.URL + record.Fields.Name = p.Name + record.Fields.Color = p.Color + record.Fields.Description = p.Description // relationships // n/a - record.Fields.Feature = features - return record } @@ -351,9 +337,7 @@ type Account struct { } func (p Account) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtabledb.Record{} - features := airtabledb.AccountRecord{} - + record := airtabledb.AccountRecord{} // base record.Fields.ID = p.ID record.Fields.CreatedAt = p.CreatedAt @@ -361,21 +345,19 @@ func (p Account) ToRecord(cache airtabledb.DB) airtabledb.Record { record.Fields.Errors = strings.Join(p.Errors, ", ") // specific - features.URL = p.URL - features.Login = p.Login - features.FullName = p.FullName - features.Type = p.Type - features.Bio = p.Bio - features.Location = p.Location - features.Company = p.Company - features.Blog = p.Blog - features.Email = p.Email - features.AvatarURL = p.AvatarURL + record.Fields.URL = p.URL + record.Fields.Login = p.Login + record.Fields.FullName = p.FullName + record.Fields.Type = p.Type + record.Fields.Bio = p.Bio + record.Fields.Location = p.Location + record.Fields.Company = p.Company + record.Fields.Blog = p.Blog + record.Fields.Email = p.Email + record.Fields.AvatarURL = p.AvatarURL // relationships - features.Provider = []string{cache.Tables[airtabledb.ProviderIndex].FindByID(p.Provider.ID)} - - record.Fields.Feature = features + record.Fields.Provider = []string{cache.Tables[airtabledb.ProviderIndex].FindByID(p.Provider.ID)} return record }