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.4 kB 358 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 DeleteLabelOps(e Execer, filters ...orm.Filter) error { 227 var conditions []string 228 var args []any 229 for _, filter := range filters { 230 conditions = append(conditions, filter.Condition()) 231 args = append(args, filter.Arg()...) 232 } 233 whereClause := "" 234 if conditions != nil { 235 whereClause = " where " + strings.Join(conditions, " and ") 236 } 237 query := fmt.Sprintf(`delete from label_ops %s`, whereClause) 238 _, err := e.Exec(query, args...) 239 return err 240} 241 242func GetLabelOps(e Execer, filters ...orm.Filter) ([]models.LabelOp, error) { 243 var labelOps []models.LabelOp 244 var conditions []string 245 var args []any 246 247 for _, filter := range filters { 248 conditions = append(conditions, filter.Condition()) 249 args = append(args, filter.Arg()...) 250 } 251 252 whereClause := "" 253 if conditions != nil { 254 whereClause = " where " + strings.Join(conditions, " and ") 255 } 256 257 query := fmt.Sprintf( 258 ` 259 select 260 id, 261 did, 262 rkey, 263 subject, 264 operation, 265 operand_key, 266 operand_value, 267 performed 268 from label_ops 269 %s 270 order by id 271 `, 272 whereClause, 273 ) 274 275 rows, err := e.Query(query, args...) 276 if err != nil { 277 return nil, err 278 } 279 defer rows.Close() 280 281 for rows.Next() { 282 var labelOp models.LabelOp 283 var performedAt string 284 285 if err := rows.Scan( 286 &labelOp.Id, 287 &labelOp.Did, 288 &labelOp.Rkey, 289 &labelOp.Subject, 290 &labelOp.Operation, 291 &labelOp.OperandKey, 292 &labelOp.OperandValue, 293 &performedAt, 294 ); err != nil { 295 return nil, err 296 } 297 298 labelOp.PerformedAt, err = time.Parse(time.RFC3339, performedAt) 299 if err != nil { 300 labelOp.PerformedAt = time.Time{} 301 } 302 303 labelOps = append(labelOps, labelOp) 304 } 305 306 return labelOps, nil 307} 308 309// get labels for a given list of subject URIs 310func GetLabels(e Execer, filters ...orm.Filter) (map[syntax.ATURI]models.LabelState, error) { 311 ops, err := GetLabelOps(e, filters...) 312 if err != nil { 313 return nil, err 314 } 315 316 // group ops by subject 317 opsBySubject := make(map[syntax.ATURI][]models.LabelOp) 318 for _, op := range ops { 319 subject := syntax.ATURI(op.Subject) 320 opsBySubject[subject] = append(opsBySubject[subject], op) 321 } 322 323 // get all unique labelats for creating the context 324 labelAtSet := make(map[string]bool) 325 for _, op := range ops { 326 labelAtSet[op.OperandKey] = true 327 } 328 labelAts := slices.Collect(maps.Keys(labelAtSet)) 329 330 actx, err := NewLabelApplicationCtx(e, orm.FilterIn("at_uri", labelAts)) 331 if err != nil { 332 return nil, err 333 } 334 335 // apply label ops for each subject and collect results 336 results := make(map[syntax.ATURI]models.LabelState) 337 for subject, subjectOps := range opsBySubject { 338 state := models.NewLabelState() 339 actx.ApplyLabelOps(state, subjectOps) 340 results[subject] = state 341 } 342 343 return results, nil 344} 345 346func NewLabelApplicationCtx(e Execer, filters ...orm.Filter) (*models.LabelApplicationCtx, error) { 347 labels, err := GetLabelDefinitions(e, filters...) 348 if err != nil { 349 return nil, err 350 } 351 352 defs := make(map[string]*models.LabelDefinition) 353 for _, l := range labels { 354 defs[l.AtUri().String()] = &l 355 } 356 357 return &models.LabelApplicationCtx{Defs: defs}, nil 358}