This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / db / label.go
7.0 kB 342 lines
1package db 2 3import ( 4 "database/sql" 5 "fmt" 6 "maps" 7 "slices" 8 "strings" 9 "time" 10 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/orm" 14) 15 16// no updating type for now 17func AddLabelDefinition(e Execer, l *models.LabelDefinition) (int64, error) { 18 result, err := e.Exec( 19 `insert into label_definitions ( 20 did, 21 rkey, 22 name, 23 value_type, 24 value_format, 25 value_enum, 26 scope, 27 color, 28 multiple, 29 created 30 ) 31 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 32 on conflict(did, rkey) do update set 33 name = excluded.name, 34 scope = excluded.scope, 35 color = excluded.color, 36 multiple = excluded.multiple`, 37 l.Did, 38 l.Rkey, 39 l.Name, 40 l.ValueType.Type, 41 l.ValueType.Format, 42 strings.Join(l.ValueType.Enum, ","), 43 strings.Join(l.Scope, ","), 44 l.Color, 45 l.Multiple, 46 l.Created.Format(time.RFC3339), 47 time.Now().Format(time.RFC3339), 48 ) 49 if err != nil { 50 return 0, err 51 } 52 53 id, err := result.LastInsertId() 54 if err != nil { 55 return 0, err 56 } 57 58 l.Id = id 59 60 return id, nil 61} 62 63func DeleteLabelDefinition(e Execer, filters ...orm.Filter) error { 64 var conditions []string 65 var args []any 66 for _, filter := range filters { 67 conditions = append(conditions, filter.Condition()) 68 args = append(args, filter.Arg()...) 69 } 70 whereClause := "" 71 if conditions != nil { 72 whereClause = " where " + strings.Join(conditions, " and ") 73 } 74 query := fmt.Sprintf(`delete from label_definitions %s`, whereClause) 75 _, err := e.Exec(query, args...) 76 return err 77} 78 79func GetLabelDefinitions(e Execer, filters ...orm.Filter) ([]models.LabelDefinition, error) { 80 var labelDefinitions []models.LabelDefinition 81 var conditions []string 82 var args []any 83 84 for _, filter := range filters { 85 conditions = append(conditions, filter.Condition()) 86 args = append(args, filter.Arg()...) 87 } 88 89 whereClause := "" 90 if conditions != nil { 91 whereClause = " where " + strings.Join(conditions, " and ") 92 } 93 94 query := fmt.Sprintf( 95 ` 96 select 97 id, 98 did, 99 rkey, 100 name, 101 value_type, 102 value_format, 103 value_enum, 104 scope, 105 color, 106 multiple, 107 created 108 from label_definitions 109 %s 110 order by created 111 `, 112 whereClause, 113 ) 114 115 rows, err := e.Query(query, args...) 116 if err != nil { 117 return nil, err 118 } 119 defer rows.Close() 120 121 for rows.Next() { 122 var labelDefinition models.LabelDefinition 123 var createdAt, enumVariants, scopes string 124 var color sql.Null[string] 125 var multiple int 126 127 if err := rows.Scan( 128 &labelDefinition.Id, 129 &labelDefinition.Did, 130 &labelDefinition.Rkey, 131 &labelDefinition.Name, 132 &labelDefinition.ValueType.Type, 133 &labelDefinition.ValueType.Format, 134 &enumVariants, 135 &scopes, 136 &color, 137 &multiple, 138 &createdAt, 139 ); err != nil { 140 return nil, err 141 } 142 143 labelDefinition.Created, err = time.Parse(time.RFC3339, createdAt) 144 if err != nil { 145 labelDefinition.Created = time.Now() 146 } 147 148 if color.Valid { 149 labelDefinition.Color = &color.V 150 } 151 152 if multiple != 0 { 153 labelDefinition.Multiple = true 154 } 155 156 if enumVariants != "" { 157 labelDefinition.ValueType.Enum = strings.Split(enumVariants, ",") 158 } 159 160 for s := range strings.SplitSeq(scopes, ",") { 161 labelDefinition.Scope = append(labelDefinition.Scope, s) 162 } 163 164 labelDefinitions = append(labelDefinitions, labelDefinition) 165 } 166 167 return labelDefinitions, nil 168} 169 170// helper to get exactly one label def 171func GetLabelDefinition(e Execer, filters ...orm.Filter) (*models.LabelDefinition, error) { 172 labels, err := GetLabelDefinitions(e, filters...) 173 if err != nil { 174 return nil, err 175 } 176 177 if labels == nil { 178 return nil, sql.ErrNoRows 179 } 180 181 if len(labels) != 1 { 182 return nil, fmt.Errorf("too many rows returned") 183 } 184 185 return &labels[0], nil 186} 187 188func AddLabelOp(e Execer, l *models.LabelOp) (int64, error) { 189 result, err := e.Exec( 190 `insert into label_ops ( 191 did, 192 rkey, 193 subject, 194 operation, 195 operand_key, 196 operand_value, 197 performed 198 ) 199 values (?, ?, ?, ?, ?, ?, ?) 200 on conflict(did, rkey, subject, operand_key, operand_value) do update set 201 operation = excluded.operation, 202 operand_value = excluded.operand_value, 203 performed = excluded.performed`, 204 l.Did, 205 l.Rkey, 206 l.Subject.String(), 207 string(l.Operation), 208 l.OperandKey, 209 l.OperandValue, 210 l.PerformedAt.Format(time.RFC3339), 211 ) 212 if err != nil { 213 return 0, err 214 } 215 216 id, err := result.LastInsertId() 217 if err != nil { 218 return 0, err 219 } 220 221 l.Id = id 222 223 return id, nil 224} 225 226func GetLabelOps(e Execer, filters ...orm.Filter) ([]models.LabelOp, error) { 227 var labelOps []models.LabelOp 228 var conditions []string 229 var args []any 230 231 for _, filter := range filters { 232 conditions = append(conditions, filter.Condition()) 233 args = append(args, filter.Arg()...) 234 } 235 236 whereClause := "" 237 if conditions != nil { 238 whereClause = " where " + strings.Join(conditions, " and ") 239 } 240 241 query := fmt.Sprintf( 242 ` 243 select 244 id, 245 did, 246 rkey, 247 subject, 248 operation, 249 operand_key, 250 operand_value, 251 performed 252 from label_ops 253 %s 254 order by id 255 `, 256 whereClause, 257 ) 258 259 rows, err := e.Query(query, args...) 260 if err != nil { 261 return nil, err 262 } 263 defer rows.Close() 264 265 for rows.Next() { 266 var labelOp models.LabelOp 267 var performedAt string 268 269 if err := rows.Scan( 270 &labelOp.Id, 271 &labelOp.Did, 272 &labelOp.Rkey, 273 &labelOp.Subject, 274 &labelOp.Operation, 275 &labelOp.OperandKey, 276 &labelOp.OperandValue, 277 &performedAt, 278 ); err != nil { 279 return nil, err 280 } 281 282 labelOp.PerformedAt, err = time.Parse(time.RFC3339, performedAt) 283 if err != nil { 284 labelOp.PerformedAt = time.Time{} 285 } 286 287 labelOps = append(labelOps, labelOp) 288 } 289 290 return labelOps, nil 291} 292 293// get labels for a given list of subject URIs 294func GetLabels(e Execer, filters ...orm.Filter) (map[syntax.ATURI]models.LabelState, error) { 295 ops, err := GetLabelOps(e, filters...) 296 if err != nil { 297 return nil, err 298 } 299 300 // group ops by subject 301 opsBySubject := make(map[syntax.ATURI][]models.LabelOp) 302 for _, op := range ops { 303 subject := syntax.ATURI(op.Subject) 304 opsBySubject[subject] = append(opsBySubject[subject], op) 305 } 306 307 // get all unique labelats for creating the context 308 labelAtSet := make(map[string]bool) 309 for _, op := range ops { 310 labelAtSet[op.OperandKey] = true 311 } 312 labelAts := slices.Collect(maps.Keys(labelAtSet)) 313 314 actx, err := NewLabelApplicationCtx(e, orm.FilterIn("at_uri", labelAts)) 315 if err != nil { 316 return nil, err 317 } 318 319 // apply label ops for each subject and collect results 320 results := make(map[syntax.ATURI]models.LabelState) 321 for subject, subjectOps := range opsBySubject { 322 state := models.NewLabelState() 323 actx.ApplyLabelOps(state, subjectOps) 324 results[subject] = state 325 } 326 327 return results, nil 328} 329 330func NewLabelApplicationCtx(e Execer, filters ...orm.Filter) (*models.LabelApplicationCtx, error) { 331 labels, err := GetLabelDefinitions(e, filters...) 332 if err != nil { 333 return nil, err 334 } 335 336 defs := make(map[string]*models.LabelDefinition) 337 for _, l := range labels { 338 defs[l.AtUri().String()] = &l 339 } 340 341 return &models.LabelApplicationCtx{Defs: defs}, nil 342}