Monorepo for Tangled tangled.org

appview/models: move db.String into models

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li 3c8a87ca c07ba238

verified
Changed files
+146 -131
appview
+5 -110
appview/db/strings.go
··· 1 1 package db 2 2 3 3 import ( 4 - "bytes" 5 4 "database/sql" 6 5 "errors" 7 6 "fmt" 8 - "io" 9 7 "strings" 10 8 "time" 11 - "unicode/utf8" 12 9 13 - "github.com/bluesky-social/indigo/atproto/syntax" 14 - "tangled.org/core/api/tangled" 10 + "tangled.org/core/appview/models" 15 11 ) 16 12 17 - type String struct { 18 - Did syntax.DID 19 - Rkey string 20 - 21 - Filename string 22 - Description string 23 - Contents string 24 - Created time.Time 25 - Edited *time.Time 26 - } 27 - 28 - func (s *String) StringAt() syntax.ATURI { 29 - return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", s.Did, tangled.StringNSID, s.Rkey)) 30 - } 31 - 32 - type StringStats struct { 33 - LineCount uint64 34 - ByteCount uint64 35 - } 36 - 37 - func (s String) Stats() StringStats { 38 - lineCount, err := countLines(strings.NewReader(s.Contents)) 39 - if err != nil { 40 - // non-fatal 41 - // TODO: log this? 42 - } 43 - 44 - return StringStats{ 45 - LineCount: uint64(lineCount), 46 - ByteCount: uint64(len(s.Contents)), 47 - } 48 - } 49 - 50 - func (s String) Validate() error { 51 - var err error 52 - 53 - if utf8.RuneCountInString(s.Filename) > 140 { 54 - err = errors.Join(err, fmt.Errorf("filename too long")) 55 - } 56 - 57 - if utf8.RuneCountInString(s.Description) > 280 { 58 - err = errors.Join(err, fmt.Errorf("description too long")) 59 - } 60 - 61 - if len(s.Contents) == 0 { 62 - err = errors.Join(err, fmt.Errorf("contents is empty")) 63 - } 64 - 65 - return err 66 - } 67 - 68 - func (s *String) AsRecord() tangled.String { 69 - return tangled.String{ 70 - Filename: s.Filename, 71 - Description: s.Description, 72 - Contents: s.Contents, 73 - CreatedAt: s.Created.Format(time.RFC3339), 74 - } 75 - } 76 - 77 - func StringFromRecord(did, rkey string, record tangled.String) String { 78 - created, err := time.Parse(record.CreatedAt, time.RFC3339) 79 - if err != nil { 80 - created = time.Now() 81 - } 82 - return String{ 83 - Did: syntax.DID(did), 84 - Rkey: rkey, 85 - Filename: record.Filename, 86 - Description: record.Description, 87 - Contents: record.Contents, 88 - Created: created, 89 - } 90 - } 91 - 92 - func AddString(e Execer, s String) error { 13 + func AddString(e Execer, s models.String) error { 93 14 _, err := e.Exec( 94 15 `insert into strings ( 95 16 did, ··· 123 44 return err 124 45 } 125 46 126 - func GetStrings(e Execer, limit int, filters ...filter) ([]String, error) { 127 - var all []String 47 + func GetStrings(e Execer, limit int, filters ...filter) ([]models.String, error) { 48 + var all []models.String 128 49 129 50 var conditions []string 130 51 var args []any ··· 167 88 defer rows.Close() 168 89 169 90 for rows.Next() { 170 - var s String 91 + var s models.String 171 92 var createdAt string 172 93 var editedAt sql.NullString 173 94 ··· 248 169 _, err := e.Exec(query, args...) 249 170 return err 250 171 } 251 - 252 - func countLines(r io.Reader) (int, error) { 253 - buf := make([]byte, 32*1024) 254 - bufLen := 0 255 - count := 0 256 - nl := []byte{'\n'} 257 - 258 - for { 259 - c, err := r.Read(buf) 260 - if c > 0 { 261 - bufLen += c 262 - } 263 - count += bytes.Count(buf[:c], nl) 264 - 265 - switch { 266 - case err == io.EOF: 267 - /* handle last line not having a newline at the end */ 268 - if bufLen >= 1 && buf[(bufLen-1)%(32*1024)] != '\n' { 269 - count++ 270 - } 271 - return count, nil 272 - case err != nil: 273 - return 0, err 274 - } 275 - } 276 - }
+2 -2
appview/ingester.go
··· 594 594 return err 595 595 } 596 596 597 - string := db.StringFromRecord(did, rkey, record) 597 + string := models.StringFromRecord(did, rkey, record) 598 598 599 - if err = string.Validate(); err != nil { 599 + if err = i.Validator.ValidateString(&string); err != nil { 600 600 l.Error("invalid record", "err", err) 601 601 return err 602 602 }
+95
appview/models/string.go
··· 1 + package models 2 + 3 + import ( 4 + "bytes" 5 + "fmt" 6 + "io" 7 + "strings" 8 + "time" 9 + 10 + "github.com/bluesky-social/indigo/atproto/syntax" 11 + "tangled.org/core/api/tangled" 12 + ) 13 + 14 + type String struct { 15 + Did syntax.DID 16 + Rkey string 17 + 18 + Filename string 19 + Description string 20 + Contents string 21 + Created time.Time 22 + Edited *time.Time 23 + } 24 + 25 + func (s *String) StringAt() syntax.ATURI { 26 + return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", s.Did, tangled.StringNSID, s.Rkey)) 27 + } 28 + 29 + func (s *String) AsRecord() tangled.String { 30 + return tangled.String{ 31 + Filename: s.Filename, 32 + Description: s.Description, 33 + Contents: s.Contents, 34 + CreatedAt: s.Created.Format(time.RFC3339), 35 + } 36 + } 37 + 38 + func StringFromRecord(did, rkey string, record tangled.String) String { 39 + created, err := time.Parse(record.CreatedAt, time.RFC3339) 40 + if err != nil { 41 + created = time.Now() 42 + } 43 + return String{ 44 + Did: syntax.DID(did), 45 + Rkey: rkey, 46 + Filename: record.Filename, 47 + Description: record.Description, 48 + Contents: record.Contents, 49 + Created: created, 50 + } 51 + } 52 + 53 + type StringStats struct { 54 + LineCount uint64 55 + ByteCount uint64 56 + } 57 + 58 + func (s String) Stats() StringStats { 59 + lineCount, err := countLines(strings.NewReader(s.Contents)) 60 + if err != nil { 61 + // non-fatal 62 + // TODO: log this? 63 + } 64 + 65 + return StringStats{ 66 + LineCount: uint64(lineCount), 67 + ByteCount: uint64(len(s.Contents)), 68 + } 69 + } 70 + 71 + func countLines(r io.Reader) (int, error) { 72 + buf := make([]byte, 32*1024) 73 + bufLen := 0 74 + count := 0 75 + nl := []byte{'\n'} 76 + 77 + for { 78 + c, err := r.Read(buf) 79 + if c > 0 { 80 + bufLen += c 81 + } 82 + count += bytes.Count(buf[:c], nl) 83 + 84 + switch { 85 + case err == io.EOF: 86 + /* handle last line not having a newline at the end */ 87 + if bufLen >= 1 && buf[(bufLen-1)%(32*1024)] != '\n' { 88 + count++ 89 + } 90 + return count, nil 91 + case err != nil: 92 + return 0, err 93 + } 94 + } 95 + }
+2 -3
appview/notify/merged_notifier.go
··· 3 3 import ( 4 4 "context" 5 5 6 - "tangled.org/core/appview/db" 7 6 "tangled.org/core/appview/models" 8 7 ) 9 8 ··· 68 67 } 69 68 } 70 69 71 - func (m *mergedNotifier) NewString(ctx context.Context, string *db.String) { 70 + func (m *mergedNotifier) NewString(ctx context.Context, string *models.String) { 72 71 for _, notifier := range m.notifiers { 73 72 notifier.NewString(ctx, string) 74 73 } 75 74 } 76 75 77 - func (m *mergedNotifier) EditString(ctx context.Context, string *db.String) { 76 + func (m *mergedNotifier) EditString(ctx context.Context, string *models.String) { 78 77 for _, notifier := range m.notifiers { 79 78 notifier.EditString(ctx, string) 80 79 }
+4 -5
appview/notify/notifier.go
··· 3 3 import ( 4 4 "context" 5 5 6 - "tangled.org/core/appview/db" 7 6 "tangled.org/core/appview/models" 8 7 ) 9 8 ··· 23 22 24 23 UpdateProfile(ctx context.Context, profile *models.Profile) 25 24 26 - NewString(ctx context.Context, s *db.String) 27 - EditString(ctx context.Context, s *db.String) 25 + NewString(ctx context.Context, s *models.String) 26 + EditString(ctx context.Context, s *models.String) 28 27 DeleteString(ctx context.Context, did, rkey string) 29 28 } 30 29 ··· 48 47 49 48 func (m *BaseNotifier) UpdateProfile(ctx context.Context, profile *models.Profile) {} 50 49 51 - func (m *BaseNotifier) NewString(ctx context.Context, s *db.String) {} 52 - func (m *BaseNotifier) EditString(ctx context.Context, s *db.String) {} 50 + func (m *BaseNotifier) NewString(ctx context.Context, s *models.String) {} 51 + func (m *BaseNotifier) EditString(ctx context.Context, s *models.String) {} 53 52 func (m *BaseNotifier) DeleteString(ctx context.Context, did, rkey string) {}
+6 -6
appview/pages/pages.go
··· 477 477 478 478 type ProfileStringsParams struct { 479 479 LoggedInUser *oauth.User 480 - Strings []db.String 480 + Strings []models.String 481 481 Card *ProfileCard 482 482 Active string 483 483 } ··· 1308 1308 Action string 1309 1309 1310 1310 // this is supplied in the case of editing an existing string 1311 - String db.String 1311 + String models.String 1312 1312 } 1313 1313 1314 1314 func (p *Pages) PutString(w io.Writer, params PutStringParams) error { ··· 1318 1318 type StringsDashboardParams struct { 1319 1319 LoggedInUser *oauth.User 1320 1320 Card ProfileCard 1321 - Strings []db.String 1321 + Strings []models.String 1322 1322 } 1323 1323 1324 1324 func (p *Pages) StringsDashboard(w io.Writer, params StringsDashboardParams) error { ··· 1327 1327 1328 1328 type StringTimelineParams struct { 1329 1329 LoggedInUser *oauth.User 1330 - Strings []db.String 1330 + Strings []models.String 1331 1331 } 1332 1332 1333 1333 func (p *Pages) StringsTimeline(w io.Writer, params StringTimelineParams) error { ··· 1339 1339 ShowRendered bool 1340 1340 RenderToggle bool 1341 1341 RenderedContents template.HTML 1342 - String db.String 1343 - Stats db.StringStats 1342 + String models.String 1343 + Stats models.StringStats 1344 1344 Owner identity.Identity 1345 1345 } 1346 1346
+2 -3
appview/posthog/notifier.go
··· 5 5 "log" 6 6 7 7 "github.com/posthog/posthog-go" 8 - "tangled.org/core/appview/db" 9 8 "tangled.org/core/appview/models" 10 9 "tangled.org/core/appview/notify" 11 10 ) ··· 142 141 } 143 142 } 144 143 145 - func (n *posthogNotifier) EditString(ctx context.Context, string *db.String) { 144 + func (n *posthogNotifier) EditString(ctx context.Context, string *models.String) { 146 145 err := n.client.Enqueue(posthog.Capture{ 147 146 DistinctId: string.Did.String(), 148 147 Event: "edit_string", ··· 153 152 } 154 153 } 155 154 156 - func (n *posthogNotifier) CreateString(ctx context.Context, string *db.String) { 155 + func (n *posthogNotifier) CreateString(ctx context.Context, string models.String) { 157 156 err := n.client.Enqueue(posthog.Capture{ 158 157 DistinctId: string.Did.String(), 159 158 Event: "create_string",
+3 -2
appview/strings/strings.go
··· 11 11 "tangled.org/core/api/tangled" 12 12 "tangled.org/core/appview/db" 13 13 "tangled.org/core/appview/middleware" 14 + "tangled.org/core/appview/models" 14 15 "tangled.org/core/appview/notify" 15 16 "tangled.org/core/appview/oauth" 16 17 "tangled.org/core/appview/pages" ··· 235 236 description := r.FormValue("description") 236 237 237 238 // construct new string from form values 238 - entry := db.String{ 239 + entry := models.String{ 239 240 Did: first.Did, 240 241 Rkey: first.Rkey, 241 242 Filename: filename, ··· 318 319 319 320 description := r.FormValue("description") 320 321 321 - string := db.String{ 322 + string := models.String{ 322 323 Did: syntax.DID(user.Did), 323 324 Rkey: tid.TID(), 324 325 Filename: filename,
+27
appview/validator/string.go
··· 1 + package validator 2 + 3 + import ( 4 + "errors" 5 + "fmt" 6 + "unicode/utf8" 7 + 8 + "tangled.org/core/appview/models" 9 + ) 10 + 11 + func (v *Validator) ValidateString(s *models.String) error { 12 + var err error 13 + 14 + if utf8.RuneCountInString(s.Filename) > 140 { 15 + err = errors.Join(err, fmt.Errorf("filename too long")) 16 + } 17 + 18 + if utf8.RuneCountInString(s.Description) > 280 { 19 + err = errors.Join(err, fmt.Errorf("description too long")) 20 + } 21 + 22 + if len(s.Contents) == 0 { 23 + err = errors.Join(err, fmt.Errorf("contents is empty")) 24 + } 25 + 26 + return err 27 + }