Monorepo for Tangled tangled.org
at sl/shared-stacks 235 lines 5.3 kB view raw
1package db 2 3import ( 4 "database/sql" 5 "fmt" 6 "strings" 7 "time" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 "tangled.org/core/appview/models" 11 "tangled.org/core/appview/pagination" 12) 13 14// NewPullRound creates new PR submission. 15// Set pullId to open a new PR. 16func NewPullRound(tx *sql.Tx, pullId int, round *models.PullRound) error { 17 // Create new PR when pullId isn't set 18 if pullId == 0 { 19 // ensure sequence exists 20 _, err := tx.Exec(` 21 insert or ignore into repo_pull_seqs (repo_at, next_pull_id) 22 values (?, 1) 23 `, round.Target.RepoAt) 24 if err != nil { 25 return err 26 } 27 28 err = tx.QueryRow(` 29 update repo_pull_seqs 30 set next_pull_id = next_pull_id + 1 31 where repo_at = ? 32 returning next_pull_id - 1 33 `, round.Target.RepoAt).Scan(&pullId) 34 if err != nil { 35 return err 36 } 37 38 _, err = tx.Exec( 39 `insert into pulls2 (pull_id, did, rkey, state) 40 values (?, ?, ?, ?)`, 41 pullId, 42 round.Did, 43 round.Rkey, 44 models.PullOpen, 45 ) 46 if err != nil { 47 return fmt.Errorf("insert pull submission: %w", err) 48 } 49 } 50 51 var sourceRepoAt, sourceBranch *string 52 if round.Source != nil { 53 x := round.Source.RepoAt.String() 54 sourceRepoAt = &x 55 sourceBranch = &round.Source.Branch 56 } 57 _, err := tx.Exec( 58 `insert into pull_rounds ( 59 pull_did, 60 pull_rkey, 61 cid, 62 target_repo_at, 63 target_branch, 64 source_repo_at, 65 source_branch, 66 patch, 67 title, 68 body, 69 created 70 ) 71 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 72 round.Did, 73 round.Rkey, 74 round.Cid, 75 round.Target.RepoAt, 76 round.Target.Branch, 77 sourceRepoAt, 78 sourceBranch, 79 round.Patch, 80 round.Title, 81 round.Body, 82 round.Created.Format(time.RFC3339), 83 ) 84 if err != nil { 85 return fmt.Errorf("insert pull submission: %w", err) 86 } 87 88 err = tx.QueryRow( 89 `select id, round from pull_rounds_view 90 where did = ? and rkey = ? and cid = ?`, 91 round.Did, round.Rkey, round.Cid, 92 ).Scan(&round.Id, &round.Round) 93 if err != nil { 94 return fmt.Errorf("get id and round number: %w", err) 95 } 96 97 if err := putReferences(tx, round.AtUri(), &round.Cid, round.References); err != nil { 98 return fmt.Errorf("put reference_links: %w", err) 99 } 100 101 return nil 102} 103 104func SetPullState2(e Execer, pullAt syntax.ATURI, state models.PullState) error { 105 _, err := e.Exec( 106 `update pulls2 set state = ? where at_uri = ? and (state <> ? or state <> ?)`, 107 state, 108 pullAt, 109 models.PullDeleted, // only update state of non-deleted pulls 110 models.PullMerged, // only update state of non-merged pulls 111 ) 112 return err 113} 114 115func ClosePull2(e Execer, pullAt syntax.ATURI) error { 116 return SetPullState2(e, pullAt, models.PullClosed) 117} 118 119func ReopenPull2(e Execer, pullAt syntax.ATURI) error { 120 return SetPullState2(e, pullAt, models.PullOpen) 121} 122 123func MergePull2(e Execer, pullAt syntax.ATURI) error { 124 return SetPullState2(e, pullAt, models.PullMerged) 125} 126 127func DeletePull2(e Execer, pullAt syntax.ATURI) error { 128 return SetPullState2(e, pullAt, models.PullDeleted) 129} 130 131func GetPulls2(e Execer, filters ...filter) ([]*models.Pull2, error) { 132 return GetPullsPaginated(e, pagination.Page{}, filters...) 133} 134 135func GetPullsPaginated(e Execer, page pagination.Page, filters ...filter) ([]*models.Pull2, error) { 136 pullsMap := make(map[syntax.ATURI]*models.Pull2) 137 138 var conditions []string 139 var args []any 140 for _, filter := range filters { 141 conditions = append(conditions, filter.Condition()) 142 args = append(args, filter.Arg()...) 143 } 144 145 whereClause := "" 146 if conditions != nil { 147 whereClause = " where " + strings.Join(conditions, " and ") 148 } 149 150 pLower := FilterGte("row_num", page.Offset+1) 151 pUpper := FilterLte("row_num", page.Offset+page.Limit) 152 153 pageClause := "" 154 if page.Limit > 0 { 155 args = append(args, pLower.Arg()...) 156 args = append(args, pUpper.Arg()...) 157 pageClause = " where " + pLower.Condition() + " and " + pUpper.Condition() 158 } 159 160 query := fmt.Sprintf( 161 `select * from ( 162 select 163 id, 164 did, 165 rkey, 166 cid, 167 target_repo_at, 168 target_branch, 169 source_repo_at, 170 source_branch, 171 patch, 172 title, 173 body, 174 created, 175 row_number() over (order by id desc) as row_num 176 from 177 pull_rounds 178 %s 179 ) ranked_pull_rounds 180 %s`, 181 whereClause, 182 pageClause, 183 ) 184 185 rows, err := e.Query(query, args...) 186 if err != nil { 187 return nil, fmt.Errorf("query pulls table: %w", err) 188 } 189 defer rows.Close() 190 191 for rows.Next() { 192 var round models.PullRound 193 var sourceBranch, sourceRepoAt sql.NullString 194 var created string 195 err := rows.Scan( 196 &round.Id, 197 &round.Did, 198 &round.Rkey, 199 &round.Cid, 200 &round.Target.RepoAt, 201 &round.Target.Branch, 202 &sourceBranch, 203 &sourceRepoAt, 204 &round.Patch, 205 &round.Title, 206 &round.Body, 207 &created, 208 ) 209 if err != nil { 210 return nil, fmt.Errorf("scan row: %w", err) 211 } 212 213 if sourceBranch.Valid && sourceRepoAt.Valid { 214 round.Source = &models.PullSource2{} 215 round.Source.Branch = sourceBranch.String 216 round.Source.RepoAt = syntax.ATURI(sourceRepoAt.String) 217 } 218 createdAtTime, _ := time.Parse(time.RFC3339, created) 219 round.Created = createdAtTime 220 221 pull, ok := pullsMap[round.AtUri()] 222 if !ok { 223 pull = &models.Pull2{ 224 Did: round.Did, 225 Rkey: round.Rkey, 226 } 227 } 228 pull.Submissions = append(pull.Submissions, &round) 229 pullsMap[round.AtUri()] = pull 230 } 231 232 // TODO: fetch pulls (id, pull_id, state) from (did, rkey) 233 234 panic("unimplemented") 235}